build: Redo the tools build
authorMatthias Clasen <mclasen@redhat.com>
Thu, 28 Jan 2021 02:56:12 +0000 (21:56 -0500)
committerMatthias Clasen <mclasen@redhat.com>
Thu, 28 Jan 2021 17:27:07 +0000 (12:27 -0500)
Move the tools directory to be toplevel, and instead of
recompiling sources twice, link them with the our new
static libgtk.a.

34 files changed:
gtk/gdkpixbufutils.c [new file with mode: 0644]
gtk/gtkiconcachevalidator.c [new file with mode: 0644]
gtk/meson.build
gtk/tools/encodesymbolic.c [deleted file]
gtk/tools/gdkpixbufutils.c [deleted file]
gtk/tools/gtk-builder-tool-enumerate.c [deleted file]
gtk/tools/gtk-builder-tool-preview.c [deleted file]
gtk/tools/gtk-builder-tool-simplify.c [deleted file]
gtk/tools/gtk-builder-tool-validate.c [deleted file]
gtk/tools/gtk-builder-tool.c [deleted file]
gtk/tools/gtk-builder-tool.h [deleted file]
gtk/tools/gtk-launch.c [deleted file]
gtk/tools/gtk-query-settings.c [deleted file]
gtk/tools/gtk4builder.its [deleted file]
gtk/tools/gtk4builder.loc [deleted file]
gtk/tools/gtk4builder.rng [deleted file]
gtk/tools/gtkiconcachevalidator.c [deleted file]
gtk/tools/meson.build [deleted file]
gtk/tools/updateiconcache.c [deleted file]
meson.build
tools/encodesymbolic.c [new file with mode: 0644]
tools/gtk-builder-tool-enumerate.c [new file with mode: 0644]
tools/gtk-builder-tool-preview.c [new file with mode: 0644]
tools/gtk-builder-tool-simplify.c [new file with mode: 0644]
tools/gtk-builder-tool-validate.c [new file with mode: 0644]
tools/gtk-builder-tool.c [new file with mode: 0644]
tools/gtk-builder-tool.h [new file with mode: 0644]
tools/gtk-launch.c [new file with mode: 0644]
tools/gtk-query-settings.c [new file with mode: 0644]
tools/gtk4builder.its [new file with mode: 0644]
tools/gtk4builder.loc [new file with mode: 0644]
tools/gtk4builder.rng [new file with mode: 0644]
tools/meson.build [new file with mode: 0644]
tools/updateiconcache.c [new file with mode: 0644]

diff --git a/gtk/gdkpixbufutils.c b/gtk/gdkpixbufutils.c
new file mode 100644 (file)
index 0000000..66a4cc4
--- /dev/null
@@ -0,0 +1,667 @@
+/* Copyright (C) 2016 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <gdk/gdk.h>
+#include "gdkpixbufutilsprivate.h"
+
+static GdkPixbuf *
+load_from_stream (GdkPixbufLoader  *loader,
+                  GInputStream     *stream,
+                  GCancellable     *cancellable,
+                  GError          **error)
+{
+  GdkPixbuf *pixbuf;
+  gssize n_read;
+  guchar buffer[65536];
+  gboolean res;
+
+  res = TRUE;
+  while (1)
+    {
+      n_read = g_input_stream_read (stream, buffer, sizeof (buffer), cancellable, error);
+      if (n_read < 0)
+        {
+          res = FALSE;
+          error = NULL; /* Ignore further errors */
+          break;
+        }
+
+      if (n_read == 0)
+        break;
+
+      if (!gdk_pixbuf_loader_write (loader, buffer, n_read, error))
+        {
+          res = FALSE;
+          error = NULL;
+          break;
+        }
+    }
+
+  if (!gdk_pixbuf_loader_close (loader, error))
+    {
+      res = FALSE;
+      error = NULL;
+    }
+
+  pixbuf = NULL;
+
+  if (res)
+    {
+      pixbuf = gdk_pixbuf_loader_get_pixbuf (loader);
+      if (pixbuf)
+        g_object_ref (pixbuf);
+    }
+
+  return pixbuf;
+}
+
+static void
+size_prepared_cb (GdkPixbufLoader *loader,
+                  int              width,
+                  int              height,
+                  gpointer         data)
+{
+  double *scale = data;
+
+  width = MAX (*scale * width, 1);
+  height = MAX (*scale * height, 1);
+
+  gdk_pixbuf_loader_set_size (loader, width, height);
+}
+
+/* Like gdk_pixbuf_new_from_stream_at_scale, but
+ * load the image at its original size times the
+ * given scale.
+ */
+GdkPixbuf *
+_gdk_pixbuf_new_from_stream_scaled (GInputStream  *stream,
+                                    const char    *format,
+                                    double         scale,
+                                    GCancellable  *cancellable,
+                                    GError       **error)
+{
+  GdkPixbufLoader *loader;
+  GdkPixbuf *pixbuf;
+
+  if (format)
+    {
+      loader = gdk_pixbuf_loader_new_with_type (format, error);
+      if (!loader)
+        return NULL;
+    }
+  else
+    loader = gdk_pixbuf_loader_new ();
+
+  if (scale != 0)
+    g_signal_connect (loader, "size-prepared",
+                      G_CALLBACK (size_prepared_cb), &scale);
+
+  pixbuf = load_from_stream (loader, stream, cancellable, error);
+
+  g_object_unref (loader);
+
+  return pixbuf;
+}
+
+static void
+size_prepared_cb2 (GdkPixbufLoader *loader,
+                   int              width,
+                   int              height,
+                   gpointer         data)
+{
+  int *scales = data;
+
+  if (scales[2]) /* keep same aspect ratio as original, while fitting in given size */
+    {
+      double aspect = (double) height / width;
+
+      /* First use given width and calculate size */
+      width = scales[0];
+      height = scales[0] * aspect;
+
+      /* Check if it fits given height, otherwise scale down */
+      if (height > scales[1])
+        {
+          width *= (double) scales[1] / height;
+          height = scales[1];
+        }
+    }
+  else
+    {
+      width = scales[0];
+      height = scales[1];
+    }
+
+  gdk_pixbuf_loader_set_size (loader, width, height);
+}
+
+GdkPixbuf *
+_gdk_pixbuf_new_from_stream_at_scale (GInputStream  *stream,
+                                      const char    *format,
+                                      int            width,
+                                      int            height,
+                                      gboolean       aspect,
+                                      GCancellable  *cancellable,
+                                      GError       **error)
+{
+  GdkPixbufLoader *loader;
+  GdkPixbuf *pixbuf;
+  int scales[3];
+
+  if (format)
+    {
+      loader = gdk_pixbuf_loader_new_with_type (format, error);
+      if (!loader)
+        return NULL;
+    }
+  else
+    loader = gdk_pixbuf_loader_new ();
+
+  scales[0] = width;
+  scales[1] = height;
+  scales[2] = aspect;
+  g_signal_connect (loader, "size-prepared",
+                    G_CALLBACK (size_prepared_cb2), scales);
+
+  pixbuf = load_from_stream (loader, stream, cancellable, error);
+
+  g_object_unref (loader);
+
+  return pixbuf;
+}
+
+GdkPixbuf *
+_gdk_pixbuf_new_from_stream (GInputStream  *stream,
+                             const char    *format,
+                             GCancellable  *cancellable,
+                             GError       **error)
+{
+  return _gdk_pixbuf_new_from_stream_scaled (stream, format, 0, cancellable, error);
+}
+
+/* Like gdk_pixbuf_new_from_resource_at_scale, but
+ * load the image at its original size times the
+ * given scale.
+ */
+GdkPixbuf *
+_gdk_pixbuf_new_from_resource_scaled (const char  *resource_path,
+                                      const char  *format,
+                                      double       scale,
+                                      GError     **error)
+{
+  GInputStream *stream;
+  GdkPixbuf *pixbuf;
+
+  stream = g_resources_open_stream (resource_path, 0, error);
+  if (stream == NULL)
+    return NULL;
+
+  pixbuf = _gdk_pixbuf_new_from_stream_scaled (stream, format, scale, NULL, error);
+  g_object_unref (stream);
+
+  return pixbuf;
+}
+
+GdkPixbuf *
+_gdk_pixbuf_new_from_resource (const char   *resource_path,
+                               const char   *format,
+                               GError      **error)
+{
+  return _gdk_pixbuf_new_from_resource_scaled (resource_path, format, 0, error);
+}
+
+GdkPixbuf *
+_gdk_pixbuf_new_from_resource_at_scale (const char   *resource_path,
+                                        const char   *format,
+                                        int           width,
+                                        int           height,
+                                        gboolean      preserve_aspect,
+                                        GError      **error)
+{
+  GInputStream *stream;
+  GdkPixbuf *pixbuf;
+
+  stream = g_resources_open_stream (resource_path, 0, error);
+  if (stream == NULL)
+    return NULL;
+
+  pixbuf = _gdk_pixbuf_new_from_stream_at_scale (stream, format, width, height, preserve_aspect, NULL, error);
+  g_object_unref (stream);
+
+  return pixbuf;
+
+}
+
+static GdkPixbuf *
+load_symbolic_svg (const char     *escaped_file_data,
+                   int             width,
+                   int             height,
+                   const char     *icon_width_str,
+                   const char     *icon_height_str,
+                   const char     *fg_string,
+                   const char     *success_color_string,
+                   const char     *warning_color_string,
+                   const char     *error_color_string,
+                   GError        **error)
+{
+  GInputStream *stream;
+  GdkPixbuf *pixbuf;
+  char *data;
+
+  data = g_strconcat ("<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n"
+                      "<svg version=\"1.1\"\n"
+                      "     xmlns=\"http://www.w3.org/2000/svg\"\n"
+                      "     xmlns:xi=\"http://www.w3.org/2001/XInclude\"\n"
+                      "     width=\"", icon_width_str, "\"\n"
+                      "     height=\"", icon_height_str, "\">\n"
+                      "  <style type=\"text/css\">\n"
+                      "    rect,circle,path {\n"
+                      "      fill: ", fg_string," !important;\n"
+                      "    }\n"
+                      "    .warning {\n"
+                      "      fill: ", warning_color_string, " !important;\n"
+                      "    }\n"
+                      "    .error {\n"
+                      "      fill: ", error_color_string ," !important;\n"
+                      "    }\n"
+                      "    .success {\n"
+                      "      fill: ", success_color_string, " !important;\n"
+                      "    }\n"
+                      "  </style>\n"
+                      "  <xi:include href=\"data:text/xml;base64,", escaped_file_data, "\"/>\n"
+                      "</svg>",
+                      NULL);
+
+  stream = g_memory_input_stream_new_from_data (data, -1, g_free);
+  pixbuf = gdk_pixbuf_new_from_stream_at_scale (stream, width, height, TRUE, NULL, error);
+  g_object_unref (stream);
+
+  return pixbuf;
+}
+
+static void
+rgba_to_pixel (const GdkRGBA *rgba,
+               guint8         pixel[4])
+{
+  pixel[0] = rgba->red * 255;
+  pixel[1] = rgba->green * 255;
+  pixel[2] = rgba->blue * 255;
+  pixel[3] = 255;
+}
+
+GdkPixbuf *
+gtk_color_symbolic_pixbuf (GdkPixbuf     *symbolic,
+                           const GdkRGBA *fg_color,
+                           const GdkRGBA *success_color,
+                           const GdkRGBA *warning_color,
+                           const GdkRGBA *error_color)
+{
+  int width, height, x, y, src_stride, dst_stride;
+  guchar *src_data, *dst_data;
+  guchar *src_row, *dst_row;
+  int alpha;
+  GdkPixbuf *colored;
+  guint8 fg_pixel[4], success_pixel[4], warning_pixel[4], error_pixel[4];
+
+  alpha = fg_color->alpha * 255;
+
+  rgba_to_pixel (fg_color, fg_pixel);
+  rgba_to_pixel (success_color, success_pixel);
+  rgba_to_pixel (warning_color, warning_pixel);
+  rgba_to_pixel (error_color, error_pixel);
+
+  width = gdk_pixbuf_get_width (symbolic);
+  height = gdk_pixbuf_get_height (symbolic);
+
+  colored = gdk_pixbuf_new (GDK_COLORSPACE_RGB, TRUE, 8, width, height);
+
+  src_stride = gdk_pixbuf_get_rowstride (symbolic);
+  src_data = gdk_pixbuf_get_pixels (symbolic);
+
+  dst_data = gdk_pixbuf_get_pixels (colored);
+  dst_stride = gdk_pixbuf_get_rowstride (colored);
+  for (y = 0; y < height; y++)
+    {
+      src_row = src_data + src_stride * y;
+      dst_row = dst_data + dst_stride * y;
+      for (x = 0; x < width; x++)
+        {
+          guint r, g, b, a;
+          int c1, c2, c3, c4;
+
+          a = src_row[3];
+          dst_row[3] = a * alpha / 255;
+
+          if (a == 0)
+            {
+              dst_row[0] = 0;
+              dst_row[1] = 0;
+              dst_row[2] = 0;
+            }
+          else
+            {
+              c2 = src_row[0];
+              c3 = src_row[1];
+              c4 = src_row[2];
+
+              if (c2 == 0 && c3 == 0 && c4 == 0)
+                {
+                  dst_row[0] = fg_pixel[0];
+                  dst_row[1] = fg_pixel[1];
+                  dst_row[2] = fg_pixel[2];
+                }
+              else
+                {
+                  c1 = 255 - c2 - c3 - c4;
+
+                  r = fg_pixel[0] * c1 + success_pixel[0] * c2 +  warning_pixel[0] * c3 +  error_pixel[0] * c4;
+                  g = fg_pixel[1] * c1 + success_pixel[1] * c2 +  warning_pixel[1] * c3 +  error_pixel[1] * c4;
+                  b = fg_pixel[2] * c1 + success_pixel[2] * c2 +  warning_pixel[2] * c3 +  error_pixel[2] * c4;
+
+                  dst_row[0] = r / 255;
+                  dst_row[1] = g / 255;
+                  dst_row[2] = b / 255;
+                }
+            }
+
+          src_row += 4;
+          dst_row += 4;
+        }
+    }
+
+  return colored;
+}
+
+static void
+extract_plane (GdkPixbuf *src,
+               GdkPixbuf *dst,
+               int        from_plane,
+               int        to_plane)
+{
+  guchar *src_data, *dst_data;
+  int width, height, src_stride, dst_stride;
+  guchar *src_row, *dst_row;
+  int x, y;
+
+  width = gdk_pixbuf_get_width (src);
+  height = gdk_pixbuf_get_height (src);
+
+  g_assert (width <= gdk_pixbuf_get_width (dst));
+  g_assert (height <= gdk_pixbuf_get_height (dst));
+
+  src_stride = gdk_pixbuf_get_rowstride (src);
+  src_data = gdk_pixbuf_get_pixels (src);
+
+  dst_data = gdk_pixbuf_get_pixels (dst);
+  dst_stride = gdk_pixbuf_get_rowstride (dst);
+
+  for (y = 0; y < height; y++)
+    {
+      src_row = src_data + src_stride * y;
+      dst_row = dst_data + dst_stride * y;
+      for (x = 0; x < width; x++)
+        {
+          dst_row[to_plane] = src_row[from_plane];
+          src_row += 4;
+          dst_row += 4;
+        }
+    }
+}
+
+GdkPixbuf *
+gtk_make_symbolic_pixbuf_from_data (const char  *file_data,
+                                    gsize        file_len,
+                                    int          width,
+                                    int          height,
+                                    double       scale,
+                                    const char  *debug_output_basename,
+                                    GError     **error)
+
+{
+  const char *r_string = "rgb(255,0,0)";
+  const char *g_string = "rgb(0,255,0)";
+  char *icon_width_str;
+  char *icon_height_str;
+  GdkPixbuf *loaded;
+  GdkPixbuf *pixbuf = NULL;
+  int plane;
+  int icon_width, icon_height;
+  char *escaped_file_data;
+
+  /* Fetch size from the original icon */
+  {
+    GInputStream *stream = g_memory_input_stream_new_from_data (file_data, file_len, NULL);
+    GdkPixbuf *reference = gdk_pixbuf_new_from_stream (stream, NULL, error);
+
+    g_object_unref (stream);
+
+    if (!reference)
+      return NULL;
+
+    icon_width = gdk_pixbuf_get_width (reference);
+    icon_height = gdk_pixbuf_get_height (reference);
+    g_object_unref (reference);
+  }
+
+  escaped_file_data = g_base64_encode ((guchar *) file_data, file_len);
+  icon_width_str = g_strdup_printf ("%d", icon_width);
+  icon_height_str = g_strdup_printf ("%d", icon_height);
+
+  if (width == 0)
+    width = icon_width * scale;
+  if (height == 0)
+    height = icon_height * scale;
+
+  for (plane = 0; plane < 3; plane++)
+    {
+      /* Here we render the svg with all colors solid, this should
+       * always make the alpha channel the same and it should match
+       * the final alpha channel for all possible renderings. We
+       * Just use it as-is for final alpha.
+       *
+       * For the 3 non-fg colors, we render once each with that
+       * color as red, and every other color as green. The resulting
+       * red will describe the amount of that color is in the
+       * opaque part of the color. We store these as the rgb
+       * channels, with the color of the fg being implicitly
+       * the "rest", as all color fractions should add up to 1.
+       */
+      loaded = load_symbolic_svg (escaped_file_data, width, height,
+                                  icon_width_str,
+                                  icon_height_str,
+                                  g_string,
+                                  plane == 0 ? r_string : g_string,
+                                  plane == 1 ? r_string : g_string,
+                                  plane == 2 ? r_string : g_string,
+                                  error);
+      if (loaded == NULL)
+        goto out;
+
+      if (debug_output_basename)
+        {
+          char *filename;
+
+          filename = g_strdup_printf ("%s.debug%d.png", debug_output_basename, plane);
+          g_print ("Writing %s\n", filename);
+          gdk_pixbuf_save (loaded, filename, "png", NULL, NULL);
+          g_free (filename);
+        }
+
+      if (pixbuf == NULL)
+        {
+          pixbuf = gdk_pixbuf_new (GDK_COLORSPACE_RGB, TRUE, 8,
+                                   gdk_pixbuf_get_width (loaded),
+                                   gdk_pixbuf_get_height (loaded));
+          gdk_pixbuf_fill (pixbuf, 0);
+        }
+
+      if (plane == 0)
+        extract_plane (loaded, pixbuf, 3, 3);
+
+      extract_plane (loaded, pixbuf, 0, plane);
+
+      g_object_unref (loaded);
+    }
+
+  g_free (escaped_file_data);
+
+out:
+  g_free (icon_width_str);
+  g_free (icon_height_str);
+
+  return pixbuf;
+}
+
+GdkPixbuf *
+gtk_make_symbolic_pixbuf_from_resource (const char  *path,
+                                        int          width,
+                                        int          height,
+                                        double       scale,
+                                        GError     **error)
+{
+  GBytes *bytes;
+  const char *data;
+  gsize size;
+  GdkPixbuf *pixbuf;
+
+  bytes = g_resources_lookup_data (path, G_RESOURCE_LOOKUP_FLAGS_NONE, error);
+  if (bytes == NULL)
+    return NULL;
+
+  data = g_bytes_get_data (bytes, &size);
+
+  pixbuf = gtk_make_symbolic_pixbuf_from_data (data, size, width, height, scale, NULL, error);
+
+  g_bytes_unref (bytes);
+
+  return pixbuf;
+}
+
+GdkPixbuf *
+gtk_make_symbolic_pixbuf_from_path (const char  *path,
+                                    int          width,
+                                    int          height,
+                                    double       scale,
+                                    GError     **error)
+{
+  char *data;
+  gsize size;
+  GdkPixbuf *pixbuf;
+
+  if (!g_file_get_contents (path, &data, &size, error))
+    return NULL;
+
+  pixbuf = gtk_make_symbolic_pixbuf_from_data (data, size, width, height, scale, NULL, error);
+
+  g_free (data);
+
+  return pixbuf;
+}
+
+GdkPixbuf *
+gtk_make_symbolic_pixbuf_from_file (GFile       *file,
+                                    int          width,
+                                    int          height,
+                                    double       scale,
+                                    GError     **error)
+{
+  char *data;
+  gsize size;
+  GdkPixbuf *pixbuf;
+
+  if (!g_file_load_contents (file, NULL, &data, &size, NULL, error))
+    return NULL;
+
+  pixbuf = gtk_make_symbolic_pixbuf_from_data (data, size, width, height, scale, NULL, error);
+
+  g_free (data);
+
+  return pixbuf;
+}
+
+GdkTexture *
+gtk_load_symbolic_texture_from_resource (const char *path)
+{
+  GdkPixbuf *pixbuf;
+  GdkTexture *texture;
+
+  pixbuf = _gdk_pixbuf_new_from_resource (path, "png", NULL);
+  texture = gdk_texture_new_for_pixbuf (pixbuf);
+  g_object_unref (pixbuf);
+
+  return texture;
+}
+
+GdkTexture *
+gtk_make_symbolic_texture_from_resource (const char  *path,
+                                         int          width,
+                                         int          height,
+                                         double       scale,
+                                         GError     **error)
+{
+  GdkPixbuf *pixbuf;
+  GdkTexture *texture = NULL;
+
+  pixbuf = gtk_make_symbolic_pixbuf_from_resource (path, width, height, scale, error);
+  if (pixbuf)
+    {
+      texture = gdk_texture_new_for_pixbuf (pixbuf);
+      g_object_unref (pixbuf);
+    }
+
+  return texture;
+}
+
+GdkTexture *
+gtk_load_symbolic_texture_from_file (GFile *file)
+{
+  GdkPixbuf *pixbuf;
+  GdkTexture *texture;
+  GInputStream *stream;
+
+  stream = G_INPUT_STREAM (g_file_read (file, NULL, NULL));
+  if (stream == NULL)
+    return NULL;
+
+  pixbuf = _gdk_pixbuf_new_from_stream (stream, "png", NULL, NULL);
+  g_object_unref (stream);
+  if (pixbuf == NULL)
+    return NULL;
+
+  texture = gdk_texture_new_for_pixbuf (pixbuf);
+  g_object_unref (pixbuf);
+
+  return texture;
+}
+
+GdkTexture *
+gtk_make_symbolic_texture_from_file (GFile       *file,
+                                     int          width,
+                                     int          height,
+                                     double       scale,
+                                     GError     **error)
+{
+  GdkPixbuf *pixbuf;
+  GdkTexture *texture;
+
+  pixbuf = gtk_make_symbolic_pixbuf_from_file (file, width, height, scale, error);
+  texture = gdk_texture_new_for_pixbuf (pixbuf);
+  g_object_unref (pixbuf);
+
+  return texture;
+}
diff --git a/gtk/gtkiconcachevalidator.c b/gtk/gtkiconcachevalidator.c
new file mode 100644 (file)
index 0000000..7cec4d4
--- /dev/null
@@ -0,0 +1,403 @@
+/* gtkiconcachevalidator.c
+ * Copyright (C) 2007 Red Hat, Inc
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library. If not, see <http://www.gnu.org/licenses/>.
+ */
+#include "config.h"
+#include "gtkiconcachevalidatorprivate.h"
+
+#include <glib.h>
+#include <gdk-pixbuf/gdk-pixdata.h>
+
+
+#define VERBOSE(x)
+
+#define check(name,condition) \
+  if (!(condition)) \
+    { \
+      VERBOSE(g_message ("bad %s", (name))); \
+      return FALSE; \
+    }
+
+static inline gboolean
+get_uint16 (CacheInfo *info,
+            guint32    offset,
+            guint16   *value)
+{
+  if (offset < info->cache_size)
+    {
+      *value = GUINT16_FROM_BE(*(guint16*)(info->cache + offset));
+      return TRUE;
+    }
+  else
+    {
+      *value = 0;
+      return FALSE;
+    }
+}
+
+static inline gboolean
+get_uint32 (CacheInfo *info,
+            guint32    offset,
+            guint32   *value)
+{
+  if (offset < info->cache_size)
+    {
+      *value = GUINT32_FROM_BE(*(guint32*)(info->cache + offset));
+      return TRUE;
+    }
+  else
+    {
+      *value = 0;
+      return FALSE;
+    }
+}
+
+static gboolean
+check_version (CacheInfo *info)
+{
+  guint16 major, minor;
+
+  check ("major version", get_uint16 (info, 0, &major) && major == 1);
+  check ("minor version", get_uint16 (info, 2, &minor) && minor == 0);
+
+  return TRUE;
+}
+
+static gboolean
+check_string (CacheInfo *info,
+              guint32    offset)
+{
+  check ("string offset", offset < info->cache_size);
+
+  if (info->flags & CHECK_STRINGS)
+    {
+      int i;
+      char c;
+
+      /* assume no string is longer than 1k */
+      for (i = 0; i < 1024; i++)
+        {
+          check ("string offset", offset + i < info->cache_size)
+          c = *(info->cache + offset + i);
+          if (c == '\0')
+            break;
+          check ("string content", g_ascii_isgraph (c));
+        }
+      check ("string length", i < 1024);
+    }
+
+  return TRUE;
+}
+
+static gboolean
+check_string_utf8 (CacheInfo *info,
+                   guint32    offset)
+{
+  check ("string offset", offset < info->cache_size);
+
+  if (info->flags & CHECK_STRINGS)
+    {
+      int i;
+      char c;
+
+      /* assume no string is longer than 1k */
+      for (i = 0; i < 1024; i++)
+        {
+          check ("string offset", offset + i < info->cache_size)
+            c = *(info->cache + offset + i);
+          if (c == '\0')
+            break;
+        }
+      check ("string length", i < 1024);
+      check ("string utf8 data", g_utf8_validate((char *)(info->cache + offset), -1, NULL));
+    }
+
+  return TRUE;
+}
+
+static gboolean
+check_directory_list (CacheInfo *info,
+                      guint32    offset)
+{
+  guint32 directory_offset;
+  int i;
+
+  check ("offset, directory list", get_uint32 (info, offset, &info->n_directories));
+
+  for (i = 0; i < info->n_directories; i++)
+    {
+      check ("offset, directory", get_uint32 (info, offset + 4 + 4 * i, &directory_offset));
+      if (!check_string (info, directory_offset))
+        return FALSE;
+    }
+
+  return TRUE;
+}
+
+static gboolean
+check_pixel_data (CacheInfo *info,
+                  guint32    offset)
+{
+  guint32 type;
+  guint32 length;
+
+  check ("offset, pixel data type", get_uint32 (info, offset, &type));
+  check ("offset, pixel data length", get_uint32 (info, offset + 4, &length));
+
+  check ("pixel data type", type == 0);
+  check ("pixel data length", offset + 8 + length < info->cache_size);
+
+  if (info->flags & CHECK_PIXBUFS)
+    {
+      GdkPixdata data;
+
+G_GNUC_BEGIN_IGNORE_DEPRECATIONS;
+      check ("pixel data", gdk_pixdata_deserialize (&data, length,
+                                                    (const guint8*)info->cache + offset + 8,
+                                                    NULL));
+G_GNUC_END_IGNORE_DEPRECATIONS;
+    }
+
+  return TRUE;
+}
+
+static gboolean
+check_embedded_rect (CacheInfo *info,
+                     guint32    offset)
+{
+  check ("embedded rect", offset + 4 < info->cache_size);
+
+  return TRUE;
+}
+
+static gboolean
+check_attach_point_list (CacheInfo *info,
+                         guint32    offset)
+{
+  guint32 n_attach_points;
+
+  check ("offset, attach point list", get_uint32 (info, offset, &n_attach_points));
+  check ("attach points", offset + 4 + 4 * n_attach_points < info->cache_size);
+
+  return TRUE;
+}
+
+static gboolean
+check_display_name_list (CacheInfo *info,
+                         guint32    offset)
+{
+  guint32 n_display_names, ofs;
+  int i;
+
+  check ("offset, display name list",
+         get_uint32 (info, offset, &n_display_names));
+  for (i = 0; i < n_display_names; i++)
+    {
+      get_uint32(info, offset + 4 + 8 * i, &ofs);
+      if (!check_string (info, ofs))
+        return FALSE;
+      get_uint32(info, offset + 4 + 8 * i + 4, &ofs);
+      if (!check_string_utf8 (info, ofs))
+        return FALSE;
+    }
+
+  return TRUE;
+}
+
+static gboolean
+check_meta_data (CacheInfo *info,
+                 guint32    offset)
+{
+  guint32 embedded_rect_offset;
+  guint32 attach_point_list_offset;
+  guint32 display_name_list_offset;
+
+  check ("offset, embedded rect",
+         get_uint32 (info, offset, &embedded_rect_offset));
+  check ("offset, attach point list",
+         get_uint32 (info, offset + 4, &attach_point_list_offset));
+  check ("offset, display name list",
+         get_uint32 (info, offset + 8, &display_name_list_offset));
+
+  if (embedded_rect_offset != 0)
+    {
+      if (!check_embedded_rect (info, embedded_rect_offset))
+        return FALSE;
+    }
+
+  if (attach_point_list_offset != 0)
+    {
+      if (!check_attach_point_list (info, attach_point_list_offset))
+        return FALSE;
+    }
+
+  if (display_name_list_offset != 0)
+    {
+      if (!check_display_name_list (info, display_name_list_offset))
+        return FALSE;
+    }
+
+  return TRUE;
+}
+
+static gboolean
+check_image_data (CacheInfo *info,
+                  guint32    offset)
+{
+  guint32 pixel_data_offset;
+  guint32 meta_data_offset;
+
+  check ("offset, pixel data", get_uint32 (info, offset, &pixel_data_offset));
+  check ("offset, meta data", get_uint32 (info, offset + 4, &meta_data_offset));
+
+  if (pixel_data_offset != 0)
+    {
+      if (!check_pixel_data (info, pixel_data_offset))
+        return FALSE;
+    }
+  if (meta_data_offset != 0)
+    {
+      if (!check_meta_data (info, meta_data_offset))
+        return FALSE;
+    }
+
+  return TRUE;
+}
+
+static gboolean
+check_image (CacheInfo *info,
+             guint32    offset)
+{
+  guint16 index;
+  guint16 flags;
+  guint32 image_data_offset;
+
+  check ("offset, image index", get_uint16 (info, offset, &index));
+  check ("offset, image flags", get_uint16 (info, offset + 2, &flags));        
+  check ("offset, image data offset",
+         get_uint32 (info, offset + 4, &image_data_offset));
+
+  check ("image index", index < info->n_directories);
+  check ("image flags", flags < 16);
+
+  if (image_data_offset != 0)
+    {
+      if (!check_image_data (info, image_data_offset))
+        return FALSE;
+    }
+
+  return TRUE;
+}
+
+static gboolean
+check_image_list (CacheInfo *info,
+                  guint32    offset)
+{
+  guint32 n_images;
+  int i;
+
+  check ("offset, image list", get_uint32 (info, offset, &n_images));
+
+  for (i = 0; i < n_images; i++)
+    {
+      if (!check_image (info, offset + 4 + 8 * i))
+        return FALSE;
+    }
+
+  return TRUE;
+}
+
+static gboolean
+check_icon (CacheInfo *info,
+            guint32    offset)
+{
+  guint32 chain_offset;
+  guint32 name_offset;
+  guint32 image_list_offset;
+
+  check ("offset, icon chain", get_uint32 (info, offset, &chain_offset));
+  check ("offset, icon name", get_uint32 (info, offset + 4, &name_offset));
+  check ("offset, icon image list", get_uint32 (info, offset + 8,
+         &image_list_offset));
+
+  if (!check_string (info, name_offset))
+    return FALSE;
+  if (!check_image_list (info, image_list_offset))
+    return FALSE;
+  if (chain_offset != 0xffffffff)
+    {
+      if (!check_icon (info, chain_offset))
+        return FALSE;
+    }
+
+  return TRUE;
+}
+
+static gboolean
+check_hash (CacheInfo *info,
+            guint32    offset)
+{
+  guint32 n_buckets, icon_offset;
+  int i;
+
+  check ("offset, hash size", get_uint32 (info, offset, &n_buckets));
+
+  for (i = 0; i < n_buckets; i++)
+    {
+      check ("offset, hash chain",
+             get_uint32 (info, offset + 4 + 4 * i, &icon_offset));
+      if (icon_offset != 0xffffffff)
+        {
+          if (!check_icon (info, icon_offset))
+            return FALSE;
+        }
+    }
+
+  return TRUE;
+}
+
+/**
+ * gtk_icon_cache_validate:
+ * @info: a CacheInfo structure
+ *
+ * Validates the icon cache passed in the @cache and
+ * @cache_size fields of the @info structure. The
+ * validator checks that offsets specified in the
+ * cache do not point outside the mapped area, that
+ * strings look reasonable, and that pixbufs can
+ * be deserialized. The amount of validation can
+ * be controlled with the @flags field.
+ *
+ * Returns: %TRUE if the cache is valid
+ */
+gboolean
+gtk_icon_cache_validate (CacheInfo *info)
+{
+  guint32 hash_offset;
+  guint32 directory_list_offset;
+
+  if (!check_version (info))
+    return FALSE;
+  check ("header, hash offset", get_uint32 (info, 4, &hash_offset));
+  check ("header, directory list offset", get_uint32 (info, 8, &directory_list_offset));
+  if (!check_directory_list (info, directory_list_offset))
+    return FALSE;
+
+  if (!check_hash (info, hash_offset))
+    return FALSE;
+
+  return TRUE;
+}
+
index ad9e8be72d4e1c1b82c25875768aec68f802873f..74a04951898841371823861d2e928aab070a55cd 100644 (file)
@@ -15,7 +15,7 @@ gtk_cargs = [
 # introspected
 gtk_private_sources = files([
   'fnmatch.c',
-  'tools/gdkpixbufutils.c',
+  'gdkpixbufutils.c',
   'gsettings-mapping.c',
   'gtkaccessibleattributeset.c',
   'gtkaccessiblevalue.c',
@@ -113,7 +113,7 @@ gtk_private_sources = files([
   'gtkgladecatalog.c',
   'gtkhsla.c',
   'gtkiconcache.c',
-  'tools/gtkiconcachevalidator.c',
+  'gtkiconcachevalidator.c',
   'gtkiconhelper.c',
   'gtkkineticscrolling.c',
   'gtkmagnifier.c',
@@ -1277,5 +1277,3 @@ libgtk_static_dep = declare_dependency(sources: gtk_dep_sources,
   link_with: [libgtk_static, libgtk_css, libgdk, libgsk ],
   link_args: common_ldflags,
 )
-
-subdir('tools')
diff --git a/gtk/tools/encodesymbolic.c b/gtk/tools/encodesymbolic.c
deleted file mode 100644 (file)
index 6f4a44f..0000000
+++ /dev/null
@@ -1,163 +0,0 @@
-/* encodesymbolic.c
- * Copyright (C) 2014  Alexander Larsson <alexl@redhat.com>
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Library General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Library General Public License for more details.
- *
- * You should have received a copy of the GNU Library General Public
- * License along with this library. If not, see <http://www.gnu.org/licenses/>.
- */
-
-#include "config.h"
-
-#include <glib.h>
-#include <gdk/gdk.h>
-#include <glib/gi18n.h>
-
-#ifdef HAVE_UNISTD_H
-#include <unistd.h>
-#endif
-#ifdef G_OS_WIN32
-#include <io.h>
-#endif
-#include <errno.h>
-#include <stdlib.h>
-#include <locale.h>
-
-#include "gdkpixbufutilsprivate.h"
-
-static char *output_dir = NULL;
-
-static gboolean debug;
-
-static GOptionEntry args[] = {
-  { "output", 'o', 0, G_OPTION_ARG_FILENAME, &output_dir, N_("Output to this directory instead of cwd"), NULL },
-  { "debug", 0, 0, G_OPTION_ARG_NONE, &debug, N_("Generate debug output") },
-  { NULL }
-};
-
-int
-main (int argc, char **argv)
-{
-  char *path, *basename, *pngpath, *pngfile, *dot;
-  GOptionContext *context;
-  GdkPixbuf *symbolic;
-  GError *error;
-  int width, height;
-  char **sizev;
-  GFileOutputStream *out;
-  GFile *dest;
-  char *data;
-  gsize len;
-
-  setlocale (LC_ALL, "");
-
-#ifdef ENABLE_NLS
-  bindtextdomain (GETTEXT_PACKAGE, GTK_LOCALEDIR);
-#ifdef HAVE_BIND_TEXTDOMAIN_CODESET
-  bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
-#endif
-#endif
-
-  g_set_prgname ("gtk-encode-symbolic-svg");
-
-  context = g_option_context_new ("[OPTION…] PATH WIDTHxHEIGHT");
-  g_option_context_add_main_entries (context, args, GETTEXT_PACKAGE);
-
-  g_option_context_parse (context, &argc, &argv, NULL);
-
-  if (argc < 3)
-    {
-      g_printerr ("%s\n", g_option_context_get_help (context, FALSE, NULL));
-      return 1;
-    }
-
-  width = 0;
-  height = 0;
-  sizev = g_strsplit (argv[2], "x", 0);
-  if (g_strv_length (sizev) == 2)
-    {
-      width = atoi(sizev[0]);
-      height = atoi(sizev[1]);
-    }
-  g_strfreev (sizev);
-
-  if (width == 0 || height == 0)
-    {
-      g_printerr (_("Invalid size %s\n"), argv[2]);
-      return 1;
-    }
-
-  path = argv[1];
-#ifdef G_OS_WIN32
-  path = g_locale_to_utf8 (path, -1, NULL, NULL, NULL);
-#endif
-
-  error = NULL;
-  if (!g_file_get_contents (path, &data, &len, &error))
-    {
-      g_printerr (_("Can’t load file: %s\n"), error->message);
-      return 1;
-    }
-
-  basename = g_path_get_basename (path);
-
-  symbolic = gtk_make_symbolic_pixbuf_from_data (data, len, width, height, 1.0, debug ? basename : NULL, &error);
-  if (symbolic == NULL)
-    {
-      g_printerr (_("Can’t load file: %s\n"), error->message);
-      return 1;
-    }
-
-  g_free (data);
-
-  dot = strrchr (basename, '.');
-  if (dot != NULL)
-    *dot = 0;
-  pngfile = g_strconcat (basename, ".symbolic.png", NULL);
-  g_free (basename);
-
-  if (output_dir != NULL)
-    pngpath = g_build_filename (output_dir, pngfile, NULL);
-  else
-    pngpath = g_strdup (pngfile);
-
-  g_free (pngfile);
-
-  dest = g_file_new_for_path (pngpath);
-
-
-  out = g_file_replace (dest,
-                       NULL, FALSE,
-                       G_FILE_CREATE_REPLACE_DESTINATION,
-                       NULL, &error);
-  if (out == NULL)
-    {
-      g_printerr (_("Can’t save file %s: %s\n"), pngpath, error->message);
-      return 1;
-    }
-
-  if (!gdk_pixbuf_save_to_stream (symbolic, G_OUTPUT_STREAM (out), "png", NULL, &error, NULL))
-    {
-      g_printerr (_("Can’t save file %s: %s\n"), pngpath, error->message);
-      return 1;
-    }
-
-  if (!g_output_stream_close (G_OUTPUT_STREAM (out), NULL, &error))
-    {
-      g_printerr (_("Can’t close stream"));
-      return 1;
-    }
-
-  g_object_unref (out);
-  g_free (pngpath);
-
-  return 0;
-}
diff --git a/gtk/tools/gdkpixbufutils.c b/gtk/tools/gdkpixbufutils.c
deleted file mode 100644 (file)
index 66a4cc4..0000000
+++ /dev/null
@@ -1,667 +0,0 @@
-/* Copyright (C) 2016 Red Hat, Inc.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library. If not, see <http://www.gnu.org/licenses/>.
- */
-
-#include "config.h"
-
-#include <gdk/gdk.h>
-#include "gdkpixbufutilsprivate.h"
-
-static GdkPixbuf *
-load_from_stream (GdkPixbufLoader  *loader,
-                  GInputStream     *stream,
-                  GCancellable     *cancellable,
-                  GError          **error)
-{
-  GdkPixbuf *pixbuf;
-  gssize n_read;
-  guchar buffer[65536];
-  gboolean res;
-
-  res = TRUE;
-  while (1)
-    {
-      n_read = g_input_stream_read (stream, buffer, sizeof (buffer), cancellable, error);
-      if (n_read < 0)
-        {
-          res = FALSE;
-          error = NULL; /* Ignore further errors */
-          break;
-        }
-
-      if (n_read == 0)
-        break;
-
-      if (!gdk_pixbuf_loader_write (loader, buffer, n_read, error))
-        {
-          res = FALSE;
-          error = NULL;
-          break;
-        }
-    }
-
-  if (!gdk_pixbuf_loader_close (loader, error))
-    {
-      res = FALSE;
-      error = NULL;
-    }
-
-  pixbuf = NULL;
-
-  if (res)
-    {
-      pixbuf = gdk_pixbuf_loader_get_pixbuf (loader);
-      if (pixbuf)
-        g_object_ref (pixbuf);
-    }
-
-  return pixbuf;
-}
-
-static void
-size_prepared_cb (GdkPixbufLoader *loader,
-                  int              width,
-                  int              height,
-                  gpointer         data)
-{
-  double *scale = data;
-
-  width = MAX (*scale * width, 1);
-  height = MAX (*scale * height, 1);
-
-  gdk_pixbuf_loader_set_size (loader, width, height);
-}
-
-/* Like gdk_pixbuf_new_from_stream_at_scale, but
- * load the image at its original size times the
- * given scale.
- */
-GdkPixbuf *
-_gdk_pixbuf_new_from_stream_scaled (GInputStream  *stream,
-                                    const char    *format,
-                                    double         scale,
-                                    GCancellable  *cancellable,
-                                    GError       **error)
-{
-  GdkPixbufLoader *loader;
-  GdkPixbuf *pixbuf;
-
-  if (format)
-    {
-      loader = gdk_pixbuf_loader_new_with_type (format, error);
-      if (!loader)
-        return NULL;
-    }
-  else
-    loader = gdk_pixbuf_loader_new ();
-
-  if (scale != 0)
-    g_signal_connect (loader, "size-prepared",
-                      G_CALLBACK (size_prepared_cb), &scale);
-
-  pixbuf = load_from_stream (loader, stream, cancellable, error);
-
-  g_object_unref (loader);
-
-  return pixbuf;
-}
-
-static void
-size_prepared_cb2 (GdkPixbufLoader *loader,
-                   int              width,
-                   int              height,
-                   gpointer         data)
-{
-  int *scales = data;
-
-  if (scales[2]) /* keep same aspect ratio as original, while fitting in given size */
-    {
-      double aspect = (double) height / width;
-
-      /* First use given width and calculate size */
-      width = scales[0];
-      height = scales[0] * aspect;
-
-      /* Check if it fits given height, otherwise scale down */
-      if (height > scales[1])
-        {
-          width *= (double) scales[1] / height;
-          height = scales[1];
-        }
-    }
-  else
-    {
-      width = scales[0];
-      height = scales[1];
-    }
-
-  gdk_pixbuf_loader_set_size (loader, width, height);
-}
-
-GdkPixbuf *
-_gdk_pixbuf_new_from_stream_at_scale (GInputStream  *stream,
-                                      const char    *format,
-                                      int            width,
-                                      int            height,
-                                      gboolean       aspect,
-                                      GCancellable  *cancellable,
-                                      GError       **error)
-{
-  GdkPixbufLoader *loader;
-  GdkPixbuf *pixbuf;
-  int scales[3];
-
-  if (format)
-    {
-      loader = gdk_pixbuf_loader_new_with_type (format, error);
-      if (!loader)
-        return NULL;
-    }
-  else
-    loader = gdk_pixbuf_loader_new ();
-
-  scales[0] = width;
-  scales[1] = height;
-  scales[2] = aspect;
-  g_signal_connect (loader, "size-prepared",
-                    G_CALLBACK (size_prepared_cb2), scales);
-
-  pixbuf = load_from_stream (loader, stream, cancellable, error);
-
-  g_object_unref (loader);
-
-  return pixbuf;
-}
-
-GdkPixbuf *
-_gdk_pixbuf_new_from_stream (GInputStream  *stream,
-                             const char    *format,
-                             GCancellable  *cancellable,
-                             GError       **error)
-{
-  return _gdk_pixbuf_new_from_stream_scaled (stream, format, 0, cancellable, error);
-}
-
-/* Like gdk_pixbuf_new_from_resource_at_scale, but
- * load the image at its original size times the
- * given scale.
- */
-GdkPixbuf *
-_gdk_pixbuf_new_from_resource_scaled (const char  *resource_path,
-                                      const char  *format,
-                                      double       scale,
-                                      GError     **error)
-{
-  GInputStream *stream;
-  GdkPixbuf *pixbuf;
-
-  stream = g_resources_open_stream (resource_path, 0, error);
-  if (stream == NULL)
-    return NULL;
-
-  pixbuf = _gdk_pixbuf_new_from_stream_scaled (stream, format, scale, NULL, error);
-  g_object_unref (stream);
-
-  return pixbuf;
-}
-
-GdkPixbuf *
-_gdk_pixbuf_new_from_resource (const char   *resource_path,
-                               const char   *format,
-                               GError      **error)
-{
-  return _gdk_pixbuf_new_from_resource_scaled (resource_path, format, 0, error);
-}
-
-GdkPixbuf *
-_gdk_pixbuf_new_from_resource_at_scale (const char   *resource_path,
-                                        const char   *format,
-                                        int           width,
-                                        int           height,
-                                        gboolean      preserve_aspect,
-                                        GError      **error)
-{
-  GInputStream *stream;
-  GdkPixbuf *pixbuf;
-
-  stream = g_resources_open_stream (resource_path, 0, error);
-  if (stream == NULL)
-    return NULL;
-
-  pixbuf = _gdk_pixbuf_new_from_stream_at_scale (stream, format, width, height, preserve_aspect, NULL, error);
-  g_object_unref (stream);
-
-  return pixbuf;
-
-}
-
-static GdkPixbuf *
-load_symbolic_svg (const char     *escaped_file_data,
-                   int             width,
-                   int             height,
-                   const char     *icon_width_str,
-                   const char     *icon_height_str,
-                   const char     *fg_string,
-                   const char     *success_color_string,
-                   const char     *warning_color_string,
-                   const char     *error_color_string,
-                   GError        **error)
-{
-  GInputStream *stream;
-  GdkPixbuf *pixbuf;
-  char *data;
-
-  data = g_strconcat ("<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n"
-                      "<svg version=\"1.1\"\n"
-                      "     xmlns=\"http://www.w3.org/2000/svg\"\n"
-                      "     xmlns:xi=\"http://www.w3.org/2001/XInclude\"\n"
-                      "     width=\"", icon_width_str, "\"\n"
-                      "     height=\"", icon_height_str, "\">\n"
-                      "  <style type=\"text/css\">\n"
-                      "    rect,circle,path {\n"
-                      "      fill: ", fg_string," !important;\n"
-                      "    }\n"
-                      "    .warning {\n"
-                      "      fill: ", warning_color_string, " !important;\n"
-                      "    }\n"
-                      "    .error {\n"
-                      "      fill: ", error_color_string ," !important;\n"
-                      "    }\n"
-                      "    .success {\n"
-                      "      fill: ", success_color_string, " !important;\n"
-                      "    }\n"
-                      "  </style>\n"
-                      "  <xi:include href=\"data:text/xml;base64,", escaped_file_data, "\"/>\n"
-                      "</svg>",
-                      NULL);
-
-  stream = g_memory_input_stream_new_from_data (data, -1, g_free);
-  pixbuf = gdk_pixbuf_new_from_stream_at_scale (stream, width, height, TRUE, NULL, error);
-  g_object_unref (stream);
-
-  return pixbuf;
-}
-
-static void
-rgba_to_pixel (const GdkRGBA *rgba,
-               guint8         pixel[4])
-{
-  pixel[0] = rgba->red * 255;
-  pixel[1] = rgba->green * 255;
-  pixel[2] = rgba->blue * 255;
-  pixel[3] = 255;
-}
-
-GdkPixbuf *
-gtk_color_symbolic_pixbuf (GdkPixbuf     *symbolic,
-                           const GdkRGBA *fg_color,
-                           const GdkRGBA *success_color,
-                           const GdkRGBA *warning_color,
-                           const GdkRGBA *error_color)
-{
-  int width, height, x, y, src_stride, dst_stride;
-  guchar *src_data, *dst_data;
-  guchar *src_row, *dst_row;
-  int alpha;
-  GdkPixbuf *colored;
-  guint8 fg_pixel[4], success_pixel[4], warning_pixel[4], error_pixel[4];
-
-  alpha = fg_color->alpha * 255;
-
-  rgba_to_pixel (fg_color, fg_pixel);
-  rgba_to_pixel (success_color, success_pixel);
-  rgba_to_pixel (warning_color, warning_pixel);
-  rgba_to_pixel (error_color, error_pixel);
-
-  width = gdk_pixbuf_get_width (symbolic);
-  height = gdk_pixbuf_get_height (symbolic);
-
-  colored = gdk_pixbuf_new (GDK_COLORSPACE_RGB, TRUE, 8, width, height);
-
-  src_stride = gdk_pixbuf_get_rowstride (symbolic);
-  src_data = gdk_pixbuf_get_pixels (symbolic);
-
-  dst_data = gdk_pixbuf_get_pixels (colored);
-  dst_stride = gdk_pixbuf_get_rowstride (colored);
-  for (y = 0; y < height; y++)
-    {
-      src_row = src_data + src_stride * y;
-      dst_row = dst_data + dst_stride * y;
-      for (x = 0; x < width; x++)
-        {
-          guint r, g, b, a;
-          int c1, c2, c3, c4;
-
-          a = src_row[3];
-          dst_row[3] = a * alpha / 255;
-
-          if (a == 0)
-            {
-              dst_row[0] = 0;
-              dst_row[1] = 0;
-              dst_row[2] = 0;
-            }
-          else
-            {
-              c2 = src_row[0];
-              c3 = src_row[1];
-              c4 = src_row[2];
-
-              if (c2 == 0 && c3 == 0 && c4 == 0)
-                {
-                  dst_row[0] = fg_pixel[0];
-                  dst_row[1] = fg_pixel[1];
-                  dst_row[2] = fg_pixel[2];
-                }
-              else
-                {
-                  c1 = 255 - c2 - c3 - c4;
-
-                  r = fg_pixel[0] * c1 + success_pixel[0] * c2 +  warning_pixel[0] * c3 +  error_pixel[0] * c4;
-                  g = fg_pixel[1] * c1 + success_pixel[1] * c2 +  warning_pixel[1] * c3 +  error_pixel[1] * c4;
-                  b = fg_pixel[2] * c1 + success_pixel[2] * c2 +  warning_pixel[2] * c3 +  error_pixel[2] * c4;
-
-                  dst_row[0] = r / 255;
-                  dst_row[1] = g / 255;
-                  dst_row[2] = b / 255;
-                }
-            }
-
-          src_row += 4;
-          dst_row += 4;
-        }
-    }
-
-  return colored;
-}
-
-static void
-extract_plane (GdkPixbuf *src,
-               GdkPixbuf *dst,
-               int        from_plane,
-               int        to_plane)
-{
-  guchar *src_data, *dst_data;
-  int width, height, src_stride, dst_stride;
-  guchar *src_row, *dst_row;
-  int x, y;
-
-  width = gdk_pixbuf_get_width (src);
-  height = gdk_pixbuf_get_height (src);
-
-  g_assert (width <= gdk_pixbuf_get_width (dst));
-  g_assert (height <= gdk_pixbuf_get_height (dst));
-
-  src_stride = gdk_pixbuf_get_rowstride (src);
-  src_data = gdk_pixbuf_get_pixels (src);
-
-  dst_data = gdk_pixbuf_get_pixels (dst);
-  dst_stride = gdk_pixbuf_get_rowstride (dst);
-
-  for (y = 0; y < height; y++)
-    {
-      src_row = src_data + src_stride * y;
-      dst_row = dst_data + dst_stride * y;
-      for (x = 0; x < width; x++)
-        {
-          dst_row[to_plane] = src_row[from_plane];
-          src_row += 4;
-          dst_row += 4;
-        }
-    }
-}
-
-GdkPixbuf *
-gtk_make_symbolic_pixbuf_from_data (const char  *file_data,
-                                    gsize        file_len,
-                                    int          width,
-                                    int          height,
-                                    double       scale,
-                                    const char  *debug_output_basename,
-                                    GError     **error)
-
-{
-  const char *r_string = "rgb(255,0,0)";
-  const char *g_string = "rgb(0,255,0)";
-  char *icon_width_str;
-  char *icon_height_str;
-  GdkPixbuf *loaded;
-  GdkPixbuf *pixbuf = NULL;
-  int plane;
-  int icon_width, icon_height;
-  char *escaped_file_data;
-
-  /* Fetch size from the original icon */
-  {
-    GInputStream *stream = g_memory_input_stream_new_from_data (file_data, file_len, NULL);
-    GdkPixbuf *reference = gdk_pixbuf_new_from_stream (stream, NULL, error);
-
-    g_object_unref (stream);
-
-    if (!reference)
-      return NULL;
-
-    icon_width = gdk_pixbuf_get_width (reference);
-    icon_height = gdk_pixbuf_get_height (reference);
-    g_object_unref (reference);
-  }
-
-  escaped_file_data = g_base64_encode ((guchar *) file_data, file_len);
-  icon_width_str = g_strdup_printf ("%d", icon_width);
-  icon_height_str = g_strdup_printf ("%d", icon_height);
-
-  if (width == 0)
-    width = icon_width * scale;
-  if (height == 0)
-    height = icon_height * scale;
-
-  for (plane = 0; plane < 3; plane++)
-    {
-      /* Here we render the svg with all colors solid, this should
-       * always make the alpha channel the same and it should match
-       * the final alpha channel for all possible renderings. We
-       * Just use it as-is for final alpha.
-       *
-       * For the 3 non-fg colors, we render once each with that
-       * color as red, and every other color as green. The resulting
-       * red will describe the amount of that color is in the
-       * opaque part of the color. We store these as the rgb
-       * channels, with the color of the fg being implicitly
-       * the "rest", as all color fractions should add up to 1.
-       */
-      loaded = load_symbolic_svg (escaped_file_data, width, height,
-                                  icon_width_str,
-                                  icon_height_str,
-                                  g_string,
-                                  plane == 0 ? r_string : g_string,
-                                  plane == 1 ? r_string : g_string,
-                                  plane == 2 ? r_string : g_string,
-                                  error);
-      if (loaded == NULL)
-        goto out;
-
-      if (debug_output_basename)
-        {
-          char *filename;
-
-          filename = g_strdup_printf ("%s.debug%d.png", debug_output_basename, plane);
-          g_print ("Writing %s\n", filename);
-          gdk_pixbuf_save (loaded, filename, "png", NULL, NULL);
-          g_free (filename);
-        }
-
-      if (pixbuf == NULL)
-        {
-          pixbuf = gdk_pixbuf_new (GDK_COLORSPACE_RGB, TRUE, 8,
-                                   gdk_pixbuf_get_width (loaded),
-                                   gdk_pixbuf_get_height (loaded));
-          gdk_pixbuf_fill (pixbuf, 0);
-        }
-
-      if (plane == 0)
-        extract_plane (loaded, pixbuf, 3, 3);
-
-      extract_plane (loaded, pixbuf, 0, plane);
-
-      g_object_unref (loaded);
-    }
-
-  g_free (escaped_file_data);
-
-out:
-  g_free (icon_width_str);
-  g_free (icon_height_str);
-
-  return pixbuf;
-}
-
-GdkPixbuf *
-gtk_make_symbolic_pixbuf_from_resource (const char  *path,
-                                        int          width,
-                                        int          height,
-                                        double       scale,
-                                        GError     **error)
-{
-  GBytes *bytes;
-  const char *data;
-  gsize size;
-  GdkPixbuf *pixbuf;
-
-  bytes = g_resources_lookup_data (path, G_RESOURCE_LOOKUP_FLAGS_NONE, error);
-  if (bytes == NULL)
-    return NULL;
-
-  data = g_bytes_get_data (bytes, &size);
-
-  pixbuf = gtk_make_symbolic_pixbuf_from_data (data, size, width, height, scale, NULL, error);
-
-  g_bytes_unref (bytes);
-
-  return pixbuf;
-}
-
-GdkPixbuf *
-gtk_make_symbolic_pixbuf_from_path (const char  *path,
-                                    int          width,
-                                    int          height,
-                                    double       scale,
-                                    GError     **error)
-{
-  char *data;
-  gsize size;
-  GdkPixbuf *pixbuf;
-
-  if (!g_file_get_contents (path, &data, &size, error))
-    return NULL;
-
-  pixbuf = gtk_make_symbolic_pixbuf_from_data (data, size, width, height, scale, NULL, error);
-
-  g_free (data);
-
-  return pixbuf;
-}
-
-GdkPixbuf *
-gtk_make_symbolic_pixbuf_from_file (GFile       *file,
-                                    int          width,
-                                    int          height,
-                                    double       scale,
-                                    GError     **error)
-{
-  char *data;
-  gsize size;
-  GdkPixbuf *pixbuf;
-
-  if (!g_file_load_contents (file, NULL, &data, &size, NULL, error))
-    return NULL;
-
-  pixbuf = gtk_make_symbolic_pixbuf_from_data (data, size, width, height, scale, NULL, error);
-
-  g_free (data);
-
-  return pixbuf;
-}
-
-GdkTexture *
-gtk_load_symbolic_texture_from_resource (const char *path)
-{
-  GdkPixbuf *pixbuf;
-  GdkTexture *texture;
-
-  pixbuf = _gdk_pixbuf_new_from_resource (path, "png", NULL);
-  texture = gdk_texture_new_for_pixbuf (pixbuf);
-  g_object_unref (pixbuf);
-
-  return texture;
-}
-
-GdkTexture *
-gtk_make_symbolic_texture_from_resource (const char  *path,
-                                         int          width,
-                                         int          height,
-                                         double       scale,
-                                         GError     **error)
-{
-  GdkPixbuf *pixbuf;
-  GdkTexture *texture = NULL;
-
-  pixbuf = gtk_make_symbolic_pixbuf_from_resource (path, width, height, scale, error);
-  if (pixbuf)
-    {
-      texture = gdk_texture_new_for_pixbuf (pixbuf);
-      g_object_unref (pixbuf);
-    }
-
-  return texture;
-}
-
-GdkTexture *
-gtk_load_symbolic_texture_from_file (GFile *file)
-{
-  GdkPixbuf *pixbuf;
-  GdkTexture *texture;
-  GInputStream *stream;
-
-  stream = G_INPUT_STREAM (g_file_read (file, NULL, NULL));
-  if (stream == NULL)
-    return NULL;
-
-  pixbuf = _gdk_pixbuf_new_from_stream (stream, "png", NULL, NULL);
-  g_object_unref (stream);
-  if (pixbuf == NULL)
-    return NULL;
-
-  texture = gdk_texture_new_for_pixbuf (pixbuf);
-  g_object_unref (pixbuf);
-
-  return texture;
-}
-
-GdkTexture *
-gtk_make_symbolic_texture_from_file (GFile       *file,
-                                     int          width,
-                                     int          height,
-                                     double       scale,
-                                     GError     **error)
-{
-  GdkPixbuf *pixbuf;
-  GdkTexture *texture;
-
-  pixbuf = gtk_make_symbolic_pixbuf_from_file (file, width, height, scale, error);
-  texture = gdk_texture_new_for_pixbuf (pixbuf);
-  g_object_unref (pixbuf);
-
-  return texture;
-}
diff --git a/gtk/tools/gtk-builder-tool-enumerate.c b/gtk/tools/gtk-builder-tool-enumerate.c
deleted file mode 100644 (file)
index feb3758..0000000
+++ /dev/null
@@ -1,75 +0,0 @@
-/*  Copyright 2015 Red Hat, Inc.
- *
- * GTK+ is free software; you can redistribute it and/or modify it
- * under the terms of the GNU Lesser General Public License as
- * published by the Free Software Foundation; either version 2 of the
- * License, or (at your option) any later version.
- *
- * GLib is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with GTK+; see the file COPYING.  If not,
- * see <http://www.gnu.org/licenses/>.
- *
- * Author: Matthias Clasen
- */
-
-#include <stdlib.h>
-#include <string.h>
-#include <errno.h>
-
-#include <glib/gi18n.h>
-#include <glib/gprintf.h>
-#include <glib/gstdio.h>
-#include <gtk/gtk.h>
-#include "gtkbuilderprivate.h"
-#include "gtk-builder-tool.h"
-
-static const char *
-object_get_id (GObject *object)
-{
-  if (GTK_IS_BUILDABLE (object))
-    return gtk_buildable_get_buildable_id (GTK_BUILDABLE (object));
-  else
-    return g_object_get_data (object, "gtk-builder-id");
-}
-
-void
-do_enumerate (int *argc, const char ***argv)
-{
-  GtkBuilder *builder;
-  GError *error = NULL;
-  int ret;
-  GSList *list, *l;
-  GObject *object;
-  const char *name;
-  const char *filename;
-
-  filename = (*argv)[1];
-
-  builder = gtk_builder_new ();
-  ret = gtk_builder_add_from_file (builder, filename, &error);
-
-  if (ret == 0)
-    {
-      g_printerr ("%s\n", error->message);
-      exit (1);
-    }
-
-  list = gtk_builder_get_objects (builder);
-  for (l = list; l; l = l->next)
-    {
-      object = l->data;
-      name = object_get_id (object);
-      if (g_str_has_prefix (name, "___") && g_str_has_suffix (name, "___"))
-        continue;
-
-      g_printf ("%s (%s)\n", name, g_type_name_from_instance ((GTypeInstance*)object));
-    }
-  g_slist_free (list);
-
-  g_object_unref (builder);
-}
diff --git a/gtk/tools/gtk-builder-tool-preview.c b/gtk/tools/gtk-builder-tool-preview.c
deleted file mode 100644 (file)
index db345c7..0000000
+++ /dev/null
@@ -1,213 +0,0 @@
-/*  Copyright 2015 Red Hat, Inc.
- *
- * GTK+ is free software; you can redistribute it and/or modify it
- * under the terms of the GNU Lesser General Public License as
- * published by the Free Software Foundation; either version 2 of the
- * License, or (at your option) any later version.
- *
- * GLib is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with GTK+; see the file COPYING.  If not,
- * see <http://www.gnu.org/licenses/>.
- *
- * Author: Matthias Clasen
- */
-
-#include <stdlib.h>
-#include <string.h>
-#include <errno.h>
-
-#include <glib/gi18n.h>
-#include <glib/gprintf.h>
-#include <glib/gstdio.h>
-#include <gtk/gtk.h>
-#include "gtkbuilderprivate.h"
-#include "gtk-builder-tool.h"
-
-
-static void
-set_window_title (GtkWindow  *window,
-                  const char *filename,
-                  const char *id)
-{
-  char *name;
-  char *title;
-
-  name = g_path_get_basename (filename);
-
-  if (id)
-    title = g_strdup_printf ("%s in %s", id, name);
-  else
-    title = g_strdup (name);
-
-  gtk_window_set_title (window, title);
-
-  g_free (title);
-  g_free (name);
-}
-
-static void
-quit_cb (GtkWidget *widget,
-         gpointer   user_data)
-{
-  gboolean *is_done = user_data;
-
-  *is_done = TRUE;
-
-  g_main_context_wakeup (NULL);
-}
-
-static void
-preview_file (const char *filename,
-              const char *id,
-              const char *cssfile)
-{
-  GtkBuilder *builder;
-  GError *error = NULL;
-  GObject *object;
-  GtkWidget *window;
-  gboolean done = FALSE;
-
-  if (cssfile)
-    {
-      GtkCssProvider *provider;
-
-      provider = gtk_css_provider_new ();
-      gtk_css_provider_load_from_path (provider, cssfile);
-
-      gtk_style_context_add_provider_for_display (gdk_display_get_default (),
-                                                  GTK_STYLE_PROVIDER (provider),
-                                                  GTK_STYLE_PROVIDER_PRIORITY_APPLICATION);
-    }
-
-  builder = gtk_builder_new ();
-  if (!gtk_builder_add_from_file (builder, filename, &error))
-    {
-      g_printerr ("%s\n", error->message);
-      exit (1);
-    }
-
-  object = NULL;
-
-  if (id)
-    {
-      object = gtk_builder_get_object (builder, id);
-    }
-  else
-    {
-      GSList *objects, *l;
-
-      objects = gtk_builder_get_objects (builder);
-      for (l = objects; l; l = l->next)
-        {
-          GObject *obj = l->data;
-
-          if (GTK_IS_WINDOW (obj))
-            {
-              object = obj;
-              break;
-            }
-          else if (GTK_IS_WIDGET (obj))
-            {
-              if (object == NULL)
-                object = obj;
-            }
-        }
-      g_slist_free (objects);
-    }
-
-  if (object == NULL)
-    {
-      if (id)
-        g_printerr ("No object with ID '%s' found\n", id);
-      else
-        g_printerr ("No previewable object found\n");
-      exit (1);
-    }
-
-  if (!GTK_IS_WIDGET (object))
-    {
-      g_printerr ("Objects of type %s can't be previewed\n", G_OBJECT_TYPE_NAME (object));
-      exit (1);
-    }
-
-  if (GTK_IS_WINDOW (object))
-    window = GTK_WIDGET (object);
-  else
-    {
-      GtkWidget *widget = GTK_WIDGET (object);
-
-      window = gtk_window_new ();
-
-      if (GTK_IS_BUILDABLE (object))
-        id = gtk_buildable_get_buildable_id (GTK_BUILDABLE (object));
-
-      set_window_title (GTK_WINDOW (window), filename, id);
-
-      g_object_ref (widget);
-      if (gtk_widget_get_parent (widget) != NULL)
-        gtk_box_remove (GTK_BOX (gtk_widget_get_parent (widget)), widget);
-      gtk_window_set_child (GTK_WINDOW (window), widget);
-      g_object_unref (widget);
-    }
-
-  gtk_window_present (GTK_WINDOW (window));
-  g_signal_connect (window, "destroy", G_CALLBACK (quit_cb), &done);
-
-  while (!done)
-    g_main_context_iteration (NULL, TRUE);
-
-  g_object_unref (builder);
-}
-
-void
-do_preview (int          *argc,
-            const char ***argv)
-{
-  GOptionContext *context;
-  char *id = NULL;
-  char *css = NULL;
-  char **filenames = NULL;
-  const GOptionEntry entries[] = {
-    { "id", 0, 0, G_OPTION_ARG_STRING, &id, NULL, NULL },
-    { "css", 0, 0, G_OPTION_ARG_FILENAME, &css, NULL, NULL },
-    { G_OPTION_REMAINING, 0, 0, G_OPTION_ARG_FILENAME_ARRAY, &filenames, NULL, NULL },
-    { NULL, }
-  };
-  GError *error = NULL;
-
-  context = g_option_context_new (NULL);
-  g_option_context_set_help_enabled (context, FALSE);
-  g_option_context_add_main_entries (context, entries, NULL);
-
-  if (!g_option_context_parse (context, argc, (char ***)argv, &error))
-    {
-      g_printerr ("%s\n", error->message);
-      g_error_free (error);
-      exit (1);
-    }
-
-  g_option_context_free (context);
-
-  if (filenames == NULL)
-    {
-      g_printerr ("No .ui file specified\n");
-      exit (1);
-    }
-
-  if (g_strv_length (filenames) > 1)
-    {
-      g_printerr ("Can only preview a single .ui file\n");
-      exit (1);
-    }
-
-  preview_file (filenames[0], id, css);
-
-  g_strfreev (filenames);
-  g_free (id);
-  g_free (css);
-}
diff --git a/gtk/tools/gtk-builder-tool-simplify.c b/gtk/tools/gtk-builder-tool-simplify.c
deleted file mode 100644 (file)
index 990377b..0000000
+++ /dev/null
@@ -1,2300 +0,0 @@
-/*  Copyright 2015 Red Hat, Inc.
- *
- * GTK+ is free software; you can redistribute it and/or modify it
- * under the terms of the GNU Lesser General Public License as
- * published by the Free Software Foundation; either version 2 of the
- * License, or (at your option) any later version.
- *
- * GLib is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with GTK+; see the file COPYING.  If not,
- * see <http://www.gnu.org/licenses/>.
- *
- * Author: Matthias Clasen
- */
-
-#include <stdlib.h>
-#include <string.h>
-#include <errno.h>
-
-#include <glib/gi18n.h>
-#include <glib/gprintf.h>
-#include <glib/gstdio.h>
-#include <gtk/gtk.h>
-#include "gtkbuilderprivate.h"
-#include "gtk-builder-tool.h"
-
-typedef struct Element Element;
-struct Element {
-  Element *parent;
-  char *element_name;
-  char **attribute_names;
-  char **attribute_values;
-  char *data;
-  GList *children;
-
-  int line_number;
-  int char_number;
-};
-
-static void
-free_element (gpointer data)
-{
-  Element *element = data;
-  g_list_free_full (element->children, free_element);
-  g_free (element->element_name);
-  g_strfreev (element->attribute_names);
-  g_strfreev (element->attribute_values);
-  g_free (element->data);
-  g_free (element);
-}
-
-typedef struct {
-  Element *root;
-  Element *current;
-  GString *value;
-  GtkBuilder *builder;
-  const char *input_filename;
-  char *output_filename;
-  FILE *output;
-  gboolean convert3to4;
-  gboolean has_gtk_requires;
-} MyParserData;
-
-static void
-start_element (GMarkupParseContext  *context,
-               const char           *element_name,
-               const char          **attribute_names,
-               const char          **attribute_values,
-               gpointer              user_data,
-               GError              **error)
-{
-  MyParserData *data = user_data;
-  Element *elt;
-
-  elt = g_new0 (Element, 1);
-  elt->parent = data->current;
-  elt->element_name = g_strdup (element_name);
-  elt->attribute_names = g_strdupv ((char **)attribute_names);
-  elt->attribute_values = g_strdupv ((char **)attribute_values);
-
-  g_markup_parse_context_get_position (context, &elt->line_number, &elt->char_number);
-
-  if (data->current)
-    data->current->children = g_list_append (data->current->children, elt);
-  data->current = elt;
-
-  if (data->root == NULL)
-    data->root = elt;
-
-  g_string_truncate (data->value, 0);
-}
-
-static void
-end_element (GMarkupParseContext  *context,
-             const char           *element_name,
-             gpointer              user_data,
-             GError              **error)
-{
-  MyParserData *data = user_data;
-
-  data->current->data = g_strdup (data->value->str);
-
-  data->current = data->current->parent;
-}
-
-static void
-text (GMarkupParseContext  *context,
-      const char           *text,
-      gsize                 text_len,
-      gpointer              user_data,
-      GError              **error)
-{
-  MyParserData *data = user_data;
-
-  if (data->value)
-    {
-      g_string_append_len (data->value, text, text_len);
-      return;
-    }
-}
-
-static GMarkupParser parser = {
-  start_element,
-  end_element,
-  text,
-  NULL,
-  NULL
-};
-
-static const char *
-canonical_boolean_value (MyParserData *data,
-                         const char   *string)
-{
-  GValue value = G_VALUE_INIT;
-  gboolean b = FALSE;
-
-  if (gtk_builder_value_from_string_type (data->builder, G_TYPE_BOOLEAN, string, &value, NULL))
-    b = g_value_get_boolean (&value);
-
-  return b ? "1" : "0";
-}
-
-typedef enum {
-  PROP_KIND_OBJECT,
-  PROP_KIND_PACKING,
-  PROP_KIND_CELL_PACKING,
-  PROP_KIND_LAYOUT
-} PropKind;
-
-static PropKind
-get_prop_kind (Element *element)
-{
-  g_assert (g_str_equal (element->element_name, "property"));
-
-  if (g_str_equal (element->parent->element_name, "packing"))
-    return PROP_KIND_PACKING;
-  else if (g_str_equal (element->parent->element_name, "layout"))
-    return PROP_KIND_LAYOUT;
-  else if (g_str_equal (element->parent->element_name, "cell-packing"))
-    return PROP_KIND_CELL_PACKING;
-  else
-    return PROP_KIND_OBJECT;
-}
-
-/* A number of properties unfortunately can't be omitted even
- * if they are nominally set to their default value. In many
- * cases, this is due to subclasses not overriding the default
- * value from the superclass.
- */
-static gboolean
-needs_explicit_setting (GParamSpec *pspec,
-                        PropKind    kind)
-{
-  struct _Prop {
-    const char *class;
-    const char *property;
-    PropKind kind;
-  } props[] = {
-    { "GtkAboutDialog", "program-name", PROP_KIND_OBJECT },
-    { "GtkCalendar", "year", PROP_KIND_OBJECT },
-    { "GtkCalendar", "month", PROP_KIND_OBJECT },
-    { "GtkCalendar", "day", PROP_KIND_OBJECT },
-    { "GtkPlacesSidebar", "show-desktop", PROP_KIND_OBJECT },
-    { "GtkRadioButton", "draw-indicator", PROP_KIND_OBJECT },
-    { "GtkWidget", "hexpand", PROP_KIND_OBJECT },
-    { "GtkWidget", "vexpand", PROP_KIND_OBJECT },
-    { "GtkGridLayoutChild", "row", PROP_KIND_LAYOUT },
-    { "GtkGridLayoutChild", "column", PROP_KIND_LAYOUT },
-  };
-  gboolean found;
-  int k;
-  const char *class_name;
-
-  class_name = g_type_name (pspec->owner_type);
-
-  found = FALSE;
-  for (k = 0; k < G_N_ELEMENTS (props); k++)
-    {
-      if (strcmp (class_name, props[k].class) == 0 &&
-          strcmp (pspec->name, props[k].property) == 0 &&
-          kind == props[k].kind)
-        {
-          found = TRUE;
-          break;
-        }
-    }
-
-  return found;
-}
-
-static gboolean
-has_attribute (Element    *elt,
-               const char *name,
-               const char *value)
-{
-  int i;
-
-  for (i = 0; elt->attribute_names[i]; i++)
-    {
-      if (strcmp (elt->attribute_names[i], name) == 0 &&
-          (value == NULL || strcmp (elt->attribute_values[i], value) == 0))
-        return TRUE;
-    }
-
-  return FALSE;
-}
-
-static gboolean
-is_cdata_property (Element *element)
-{
-  if (g_str_equal (element->element_name, "property") &&
-      has_attribute (element, "name", "bytes") &&
-      element->parent != NULL &&
-      g_str_equal (element->parent->element_name, "object") &&
-      has_attribute (element->parent, "class", "GtkBuilderListItemFactory"))
-    return TRUE;
-
-  return FALSE;
-}
-
-static gboolean
-is_pcdata_element (Element *element)
-{
-  /* elements that can contain text */
-  const char *names[] = {
-    "property",
-    "attribute",
-    "action-widget",
-    "pattern",
-    "mime-type",
-    "col",
-    "item",
-    "mark",
-    NULL,
-  };
-
-  if (g_str_equal (element->element_name, "property") &&
-      (g_strv_contains ((const char * const *)element->attribute_names, "bind-source") ||
-       g_strv_contains ((const char * const *)element->attribute_names, "bind_source")))
-    return FALSE;
-
-  if (g_strv_contains (names, element->element_name))
-    return TRUE;
-
-  return FALSE;
-}
-
-static gboolean
-is_container_element (Element *element)
-{
-  /* elements that just hold a list of things and
-   * can be omitted when they have no children
-   */
-  const char *names[] = {
-    "packing",
-    "layout",
-    "cell-packing",
-    "attributes",
-    "action-widgets",
-    "patterns",
-    "mime-types",
-    "attributes",
-    "row",
-    "items",
-    NULL
-  };
-
-  if (g_strv_contains (names, element->element_name))
-    return TRUE;
-
-  return FALSE;
-}
-
-static void
-canonicalize_key (char *key)
-{
-  char *p;
-
-  for (p = key; *p != 0; p++)
-    {
-      char c = *p;
-
-      /* We may meet something like AtkObject::accessible-name */
-      if (c == ':' && ((p > key && p[-1] == ':') || p[1] == ':'))
-        continue;
-
-      if (c != '-' &&
-          (c < '0' || c > '9') &&
-          (c < 'A' || c > 'Z') &&
-          (c < 'a' || c > 'z'))
-        *p = '-';
-    }
-}
-
-static struct {
-  const char *class;
-  const char *layout_manager;
-} layout_managers[] = {
-  { "GtkBox", "GtkBoxLayout" },
-  { "GtkGrid", "GtkGridLayout" },
-  { "GtkFixed", "GtkFixedLayout" },
-  { "GtkFileChooserButton", "GtkBinLayout" },
-  { "GtkFileChooserWidget", "GtkBinLayout" },
-  { "GtkOverlay", "GtkOverlayLayout" }
-};
-
-static GParamSpec *
-get_property_pspec (MyParserData *data,
-                    const char   *class_name,
-                    const char   *property_name,
-                    PropKind      kind)
-{
-  GType type;
-  GObjectClass *class;
-  GParamSpec *pspec;
-  char *canonical_name;
-
-  type = g_type_from_name (class_name);
-  if (type == G_TYPE_INVALID)
-    {
-      type = gtk_builder_get_type_from_name (data->builder, class_name);
-      if (type == G_TYPE_INVALID)
-        return NULL;
-    }
-
-  class = g_type_class_ref (type);
-  canonical_name = g_strdup (property_name);
-  canonicalize_key (canonical_name);
-  switch (kind)
-    {
-    case PROP_KIND_OBJECT:
-      pspec = g_object_class_find_property (class, canonical_name);
-      break;
-
-    case PROP_KIND_PACKING:
-      pspec = NULL;
-      break;
-
-    case PROP_KIND_CELL_PACKING:
-      {
-        GObjectClass *cell_class;
-
-        /* We're just assuming that the cell layout is using a GtkCellAreaBox. */
-        cell_class = g_type_class_ref (GTK_TYPE_CELL_AREA_BOX);
-        pspec = gtk_cell_area_class_find_cell_property (GTK_CELL_AREA_CLASS (cell_class), canonical_name);
-        g_type_class_unref (cell_class);
-      }
-      break;
-
-    case PROP_KIND_LAYOUT:
-      {
-        int i;
-        const char *layout_manager = NULL;
-
-        pspec = NULL;
-
-        for (i = 0; i < G_N_ELEMENTS (layout_managers); i++)
-          {
-            if (g_str_equal (layout_managers[i].class, class_name))
-              {
-                layout_manager = layout_managers[i].layout_manager;
-                break;
-              }
-          }
-
-        if (layout_manager)
-          {
-            GtkLayoutManagerClass *layout_manager_class;
-
-            layout_manager_class = GTK_LAYOUT_MANAGER_CLASS (g_type_class_ref (g_type_from_name (layout_manager)));
-            if (layout_manager_class->layout_child_type != G_TYPE_INVALID)
-              {
-                GObjectClass *layout_child_class;
-                layout_child_class = g_type_class_ref (layout_manager_class->layout_child_type);
-                pspec = g_object_class_find_property (layout_child_class, canonical_name);
-                g_type_class_unref (layout_child_class);
-              }
-            g_type_class_unref (layout_manager_class);
-          }
-      }
-      break;
-
-    default:
-      g_assert_not_reached ();
-    }
-  g_free (canonical_name);
-  g_type_class_unref (class);
-
-  return pspec;
-}
-
-static const char *get_class_name (Element *element);
-
-static gboolean
-value_is_default (Element      *element,
-                  MyParserData *data,
-                  GParamSpec   *pspec,
-                  const char   *value_string)
-{
-  GValue value = { 0, };
-  gboolean ret;
-  GError *error = NULL;
-
-  if (g_type_is_a (G_PARAM_SPEC_VALUE_TYPE (pspec), G_TYPE_OBJECT))
-    return FALSE;
-
-  if (g_type_is_a (G_PARAM_SPEC_VALUE_TYPE (pspec), G_TYPE_BOXED))
-    return FALSE;
-
-  if (!gtk_builder_value_from_string (data->builder, pspec, value_string, &value, &error))
-    {
-      g_printerr (_("%s:%d: Couldn’t parse value for property '%s': %s\n"), data->input_filename, element->line_number, pspec->name, error->message);
-      g_error_free (error);
-      ret = FALSE;
-    }
-  else
-    {
-      /* GtkWidget::visible has a 'smart' default */
-      if (pspec->owner_type == GTK_TYPE_WIDGET &&
-          g_str_equal (pspec->name, "visible"))
-        {
-          const char *class_name = get_class_name (element);
-          GType type = g_type_from_name (class_name);
-          gboolean default_value;
-
-          if (g_type_is_a (type, GTK_TYPE_ROOT) ||
-              g_type_is_a (type, GTK_TYPE_POPOVER))
-            default_value = FALSE;
-          else
-            default_value = TRUE;
-
-          ret = g_value_get_boolean (&value) == default_value;
-        }
-      else if (pspec->owner_type == GTK_TYPE_WINDOW &&
-               (g_str_equal (pspec->name, "default-width") ||
-                g_str_equal (pspec->name, "default-height")))
-        {
-          int default_size;
-
-          default_size = g_value_get_int (&value);
-          ret = default_size <= 0;
-        }
-      else
-        ret = g_param_value_defaults (pspec, &value);
-    }
-
-  g_value_reset (&value);
-
-  return ret;
-}
-
-static const char *
-get_attribute_value (Element *element,
-                     const char *name)
-{
-  int i;
-
-  for (i = 0; element->attribute_names[i]; i++)
-    {
-      if (g_str_equal (element->attribute_names[i], name))
-        return element->attribute_values[i];
-    }
-
-  return NULL;
-}
-
-static void
-set_attribute_value (Element *element,
-                     const char *name,
-                     const char *value)
-{
-  int i;
-  int len;
-
-  for (i = 0; element->attribute_names[i]; i++)
-    {
-      if (g_str_equal (element->attribute_names[i], name))
-        {
-          g_free (element->attribute_values[i]);
-          element->attribute_values[i] = g_strdup (value);
-          return;
-        }
-    }
-
-  len = g_strv_length (element->attribute_names);
-  element->attribute_names = g_realloc_n (element->attribute_names, len + 2, sizeof (char *));
-  element->attribute_values = g_realloc_n (element->attribute_values, len + 2, sizeof (char *));
-  element->attribute_names[len] = g_strdup (name);
-  element->attribute_values[len] = g_strdup (value);
-  element->attribute_names[len + 1] = NULL;
-  element->attribute_values[len + 1] = NULL;
-}
-
-static gboolean
-element_is_object_or_template (Element *element)
-{
-  return g_str_equal (element->element_name, "object") ||
-         g_str_equal (element->element_name, "template");
-}
-
-static const char *
-get_class_name (Element *element)
-{
-  Element *parent = element->parent;
-
-  if (element_is_object_or_template (element))
-    parent = element;
-
-  if (g_str_equal (parent->element_name, "packing"))
-    parent = parent->parent->parent; /* child - object */
-  else if (g_str_equal (parent->element_name, "layout"))
-    parent = parent->parent->parent->parent; /* object - child - object */
-
-
-  if (g_str_equal (parent->element_name, "object"))
-    {
-      return get_attribute_value (parent, "class");
-    }
-  else if (g_str_equal (parent->element_name, "template"))
-    {
-      if (get_attribute_value (parent, "parent"))
-        return get_attribute_value (parent, "parent");
-      else
-        return get_attribute_value (parent, "class");
-    }
-
-  return NULL;
-}
-
-static gboolean
-property_is_boolean (Element      *element,
-                     MyParserData *data)
-{
-  GParamSpec *pspec = NULL;
-  const char *class_name;
-  const char *property_name;
-  int i;
-  PropKind kind;
-
-  kind = get_prop_kind (element);
-  class_name = get_class_name (element);
-  property_name = "";
-
-  for (i = 0; element->attribute_names[i]; i++)
-    {
-      if (strcmp (element->attribute_names[i], "name") == 0)
-        property_name = (const char *)element->attribute_values[i];
-    }
-
-  if (class_name && property_name)
-    pspec = get_property_pspec (data, class_name, property_name, kind);
-  if (pspec)
-    return G_PARAM_SPEC_VALUE_TYPE (pspec) == G_TYPE_BOOLEAN;
-
-  return FALSE;
-}
-
-static void
-warn_missing_property (Element      *element,
-                       MyParserData *data,
-                       const char   *class_name,
-                       const char   *property_name,
-                       PropKind      kind)
-{
-  const char *kind_str[] = { "", "Packing ", "Cell ", "Layout " };
-
-  g_printerr (_("%s:%d: %sproperty %s::%s not found\n"),
-              data->input_filename, element->line_number, kind_str[kind], class_name, property_name);
-}
-
-static gboolean
-property_can_be_omitted (Element      *element,
-                         MyParserData *data)
-{
-  int i;
-  gboolean bound;
-  gboolean translatable;
-  const char *class_name;
-  const char *property_name;
-  const char *value_string;
-  GParamSpec *pspec;
-  PropKind kind;
-
-  kind = get_prop_kind (element);
-  class_name = get_class_name (element);
-  property_name = "";
-  value_string = element->data;
-
-  bound = FALSE;
-  translatable = FALSE;
-  for (i = 0; element->attribute_names[i]; i++)
-    {
-      if (strcmp (element->attribute_names[i], "bind-source") == 0 ||
-          strcmp (element->attribute_names[i], "bind_source") == 0)
-        bound = TRUE;
-      else if (strcmp (element->attribute_names[i], "translatable") == 0)
-        translatable = TRUE;
-      else if (strcmp (element->attribute_names[i], "name") == 0)
-        property_name = (const char *)element->attribute_values[i];
-    }
-
-  if (translatable)
-    return FALSE;
-
-  if (bound)
-    return FALSE;
-
-  pspec = get_property_pspec (data, class_name, property_name, kind);
-  if (pspec == NULL)
-    {
-      warn_missing_property (element, data, class_name, property_name, kind);
-      return FALSE;
-    }
-
-  if (needs_explicit_setting (pspec, kind))
-    return FALSE;
-
-  return value_is_default (element, data, pspec, value_string);
-}
-
-static gboolean
-property_has_been_removed (Element      *element,
-                           MyParserData *data)
-{
-  const char *class_name;
-  const char *property_name;
-  struct _Prop {
-    const char *class;
-    const char *property;
-    PropKind kind;
-  } props[] = {
-    { "GtkActionBar", "position", PROP_KIND_PACKING },
-    { "GtkButtonBox", "secondary", PROP_KIND_PACKING },
-    { "GtkButtonBox", "non-homogeneous", PROP_KIND_PACKING },
-    { "GtkBox", "position", PROP_KIND_PACKING },
-    { "GtkBox", "pack-type", PROP_KIND_PACKING },
-    { "GtkHeaderBar", "position", PROP_KIND_PACKING },
-    { "GtkPopoverMenu", "position",PROP_KIND_PACKING },
-    { "GtkCheckButton", "draw-indicator", PROP_KIND_OBJECT },
-  };
-  char *canonical_name;
-  gboolean found;
-  int i, k;
-  PropKind kind;
-
-  kind = get_prop_kind (element);
-
-  class_name = get_class_name (element);
-  property_name = "";
-
-  for (i = 0; element->attribute_names[i]; i++)
-    {
-      if (strcmp (element->attribute_names[i], "name") == 0)
-        property_name = (const char *)element->attribute_values[i];
-    }
-
-  canonical_name = g_strdup (property_name);
-  g_strdelimit (canonical_name, "_", '-');
-
-  found = FALSE;
-  for (k = 0; k < G_N_ELEMENTS (props); k++)
-    {
-      if (strcmp (class_name, props[k].class) == 0 &&
-          strcmp (canonical_name, props[k].property) == 0 &&
-          kind == props[k].kind)
-        {
-          found = TRUE;
-          break;
-        }
-    }
-
-  g_free (canonical_name);
-
-  return found;
-}
-
-static void
-maybe_rename_property (Element *element, MyParserData *data)
-{
-  const char *class_name;
-  const char *property_name;
-  struct _Prop {
-    const char *class;
-    const char *property;
-    GType type;
-    PropKind kind;
-    const char *new_name;
-    const char *alt_names[3];
-  } props[] = {
-    /* the "replacement" property is placed *after* the "added" properties */
-    { "GtkPopover", "modal", GTK_TYPE_POPOVER, PROP_KIND_OBJECT, "autohide", { NULL, NULL, NULL } },
-    { "GtkWidget", "expand", GTK_TYPE_WIDGET, PROP_KIND_OBJECT, "vexpand", { "hexpand", NULL, NULL } },
-    { "GtkWidget", "margin", GTK_TYPE_WIDGET, PROP_KIND_OBJECT, "margin-bottom", { "margin-start", "margin-end", "margin-top" } },
-    { "GtkWidget", "margin-left", GTK_TYPE_WIDGET, PROP_KIND_OBJECT, "margin-start", { NULL, NULL, NULL } },
-    { "GtkWidget", "margin-right", GTK_TYPE_WIDGET, PROP_KIND_OBJECT, "margin-end", { NULL, NULL, NULL } },
-    { "GtkHeaderBar", "show-close-button", GTK_TYPE_HEADER_BAR, PROP_KIND_OBJECT, "show-title-buttons", { NULL, NULL, NULL } },
-    { "GtkHeaderBar", "custom-title", GTK_TYPE_HEADER_BAR, PROP_KIND_OBJECT, "title-widget", { NULL, NULL, NULL } },
-    { "GtkStack", "homogeneous", GTK_TYPE_STACK, PROP_KIND_OBJECT, "hhomogeneous", { "vhomogeneous", NULL, NULL } },
-    { "GtkImage", "pixbuf", GTK_TYPE_IMAGE, PROP_KIND_OBJECT, "file", { NULL, NULL, NULL } },
-  };
-  int i, k, l;
-  PropKind kind;
-  int prop_name_index = 0;
-  GType type;
-  char *canonical_name;
-
-  kind = get_prop_kind (element);
-
-  class_name = get_class_name (element);
-  property_name = NULL;
-
-  for (i = 0; element->attribute_names[i]; i++)
-    {
-      if (strcmp (element->attribute_names[i], "name") == 0)
-        {
-          prop_name_index = i;
-          property_name = (const char *)element->attribute_values[i];
-        }
-    }
-
-  if (property_name == NULL)
-    return;
-
-  type = g_type_from_name (class_name);
-
-  canonical_name = g_strdup (property_name);
-  g_strdelimit (canonical_name, "_", '-');
-
-  for (k = 0; k < G_N_ELEMENTS (props); k++)
-    {
-      if (g_type_is_a (type, props[k].type) &&
-          strcmp (canonical_name, props[k].property) == 0 &&
-          kind == props[k].kind)
-        {
-          g_free (element->attribute_values[prop_name_index]);
-          element->attribute_values[prop_name_index] = g_strdup (props[k].new_name);
-          for (l = 0; l < 3 && props[k].alt_names[l]; l++)
-            {
-              Element *elt;
-              GList *sibling;
-
-              elt = g_new0 (Element, 1);
-              elt->parent = element->parent;
-              elt->element_name = g_strdup (element->element_name);
-              elt->attribute_names = g_strdupv ((char **) element->attribute_names);
-              elt->attribute_values = g_strdupv ((char **) element->attribute_values);
-              elt->data = g_strdup (element->data);
-
-              g_free (elt->attribute_values[prop_name_index]);
-              elt->attribute_values[prop_name_index] = g_strdup (props[k].alt_names[l]);
-
-              sibling = g_list_find (element->parent->children, element);
-              element->parent->children = g_list_insert_before (element->parent->children,
-                                                                sibling,
-                                                                elt);
-            }
-          break;
-        }
-    }
-
-  g_free (canonical_name);
-}
-
-static Element *
-rewrite_stack_child (Element *child, MyParserData *data)
-{
-  Element *object = NULL;
-  Element *packing = NULL;
-  Element *new_object;
-  Element *prop;
-  GList *l;
-
-  if (!g_str_equal (child->element_name, "child"))
-    return child;
-
-  for (l = child->children; l; l = l->next)
-    {
-      Element *elt = l->data;
-      if (g_str_equal (elt->element_name, "object"))
-        object = elt;
-      else if (g_str_equal (elt->element_name, "packing"))
-        packing = elt;
-    }
-
-  if (!packing)
-    return child;
-
-  new_object = g_new0 (Element, 1);
-  new_object->element_name = g_strdup ("object");
-  new_object->attribute_names = g_new0 (char *, 2);
-  new_object->attribute_names[0] = g_strdup ("class");
-  new_object->attribute_values = g_new0 (char *, 2);
-  new_object->attribute_values[0] = g_strdup ("GtkStackPage");
-  new_object->children = packing->children;
-  new_object->parent = child;
-  packing->children = NULL;
-
-  prop = g_new0 (Element, 1);
-  prop->element_name = g_strdup ("property");
-  prop->attribute_names = g_new0 (char *, 2);
-  prop->attribute_names[0] = g_strdup ("name");
-  prop->attribute_values = g_new0 (char *, 2);
-  prop->attribute_values[0] = g_strdup ("child");
-  prop->children = g_list_append (prop->children, object);
-  prop->parent = new_object;
-  new_object->children = g_list_append (new_object->children, prop);
-
-  g_list_free (child->children);
-  child->children = g_list_append (NULL, new_object);
-
-  return child;
-}
-
-static void
-rewrite_stack (Element      *element,
-               MyParserData *data)
-{
-  GList *l, *new_children;
-
-  new_children = NULL;
-  for (l = element->children; l; l = l->next)
-    {
-      Element *child = l->data;
-      new_children = g_list_append (new_children, rewrite_stack_child (child, data));
-    }
-
-  g_list_free (element->children);
-  element->children = new_children;
-}
-
-static Element *
-rewrite_assistant_child (Element *child, MyParserData *data)
-{
-  Element *object = NULL;
-  Element *packing = NULL;
-  Element *new_object;
-  Element *prop;
-  GList *l;
-
-  if (!g_str_equal (child->element_name, "child"))
-    return child;
-
-  for (l = child->children; l; l = l->next)
-    {
-      Element *elt = l->data;
-      if (g_str_equal (elt->element_name, "object"))
-        object = elt;
-      else if (g_str_equal (elt->element_name, "packing"))
-        packing = elt;
-    }
-
-  if (!packing)
-    return child;
-
-  new_object = g_new0 (Element, 1);
-  new_object->element_name = g_strdup ("object");
-  new_object->attribute_names = g_new0 (char *, 2);
-  new_object->attribute_names[0] = g_strdup ("class");
-  new_object->attribute_values = g_new0 (char *, 2);
-  new_object->attribute_values[0] = g_strdup ("GtkAssistantPage");
-  new_object->children = packing->children;
-  new_object->parent = child;
-  packing->children = NULL;
-
-  prop = g_new0 (Element, 1);
-  prop->element_name = g_strdup ("property");
-  prop->attribute_names = g_new0 (char *, 2);
-  prop->attribute_names[0] = g_strdup ("name");
-  prop->attribute_values = g_new0 (char *, 2);
-  prop->attribute_values[0] = g_strdup ("child");
-  prop->children = g_list_append (prop->children, object);
-  prop->parent = new_object;
-  new_object->children = g_list_append (new_object->children, prop);
-
-  g_list_free (child->children);
-  child->children = g_list_append (NULL, new_object);
-
-  return child;
-}
-
-static void
-rewrite_assistant (Element      *element,
-                   MyParserData *data)
-{
-  GList *l, *new_children;
-
-  new_children = NULL;
-  for (l = element->children; l; l = l->next)
-    {
-      Element *child = l->data;
-      new_children = g_list_append (new_children, rewrite_assistant_child (child, data));
-    }
-
-  g_list_free (element->children);
-  element->children = new_children;
-}
-
-static Element *
-rewrite_notebook_page (Element *child, Element *tab, MyParserData *data)
-{
-  Element *object = NULL;
-  Element *tab_obj = NULL;
-  Element *packing = NULL;
-  Element *new_object;
-  Element *prop;
-  GList *l;
-
-  if (!g_str_equal (child->element_name, "child"))
-    return child;
-
-  if (has_attribute (child, "type", "tab") ||
-      has_attribute (child, "type", "action-start") ||
-      has_attribute (child, "type", "action-end"))
-    return child;
-
-  for (l = child->children; l; l = l->next)
-    {
-      Element *elt = l->data;
-      if (g_str_equal (elt->element_name, "object"))
-        object = elt;
-      else if (g_str_equal (elt->element_name, "packing"))
-        packing = elt;
-    }
-
-  if (!packing && !tab)
-    return child;
-
-  if (tab)
-    {
-      for (l = tab->children; l; l = l->next)
-        {
-          Element *elt = l->data;
-          if (g_str_equal (elt->element_name, "object"))
-            tab_obj = elt;
-        }
-    }
-
-  new_object = g_new0 (Element, 1);
-  new_object->element_name = g_strdup ("object");
-  new_object->attribute_names = g_new0 (char *, 2);
-  new_object->attribute_names[0] = g_strdup ("class");
-  new_object->attribute_values = g_new0 (char *, 2);
-  new_object->attribute_values[0] = g_strdup ("GtkNotebookPage");
-  new_object->parent = child;
-  if (packing)
-    {
-      new_object->children = packing->children;
-      packing->children = NULL;
-    }
-
-  prop = g_new0 (Element, 1);
-  prop->element_name = g_strdup ("property");
-  prop->attribute_names = g_new0 (char *, 2);
-  prop->attribute_names[0] = g_strdup ("name");
-  prop->attribute_values = g_new0 (char *, 2);
-  prop->attribute_values[0] = g_strdup ("child");
-  prop->children = g_list_append (prop->children, object);
-  prop->parent = new_object;
-  new_object->children = g_list_append (new_object->children, prop);
-
-  if (tab_obj)
-    {
-      prop = g_new0 (Element, 1);
-      prop->element_name = g_strdup ("property");
-      prop->attribute_names = g_new0 (char *, 2);
-      prop->attribute_names[0] = g_strdup ("name");
-      prop->attribute_values = g_new0 (char *, 2);
-      prop->attribute_values[0] = g_strdup ("tab");
-      prop->children = g_list_append (prop->children, tab_obj);
-      prop->parent = new_object;
-      new_object->children = g_list_append (new_object->children, prop);
-    }
-
-  g_list_free (child->children);
-  child->children = g_list_append (NULL, new_object);
-
-  return child;
-}
-
-static void
-rewrite_notebook (Element      *element,
-                  MyParserData *data)
-{
-  GList *l, *new_children;
-
-  new_children = NULL;
-  for (l = element->children; l; l = l->next)
-    {
-      Element *child = l->data;
-      Element *tab = l->next ? l->next->data : NULL;
-
-      if (tab && has_attribute (tab, "type", "tab"))
-        {
-          new_children = g_list_append (new_children, rewrite_notebook_page (child, tab, data));
-          l = l->next; /* skip the tab */
-        }
-      else
-        new_children = g_list_append (new_children, rewrite_notebook_page (child, NULL, data));
-    }
-
-  g_list_free (element->children);
-  element->children = new_children;
-}
-
-static void
-rewrite_pack_type_child (Element *element,
-                         MyParserData *data)
-{
-  Element *pack_type = NULL;
-  GList *l, *ll;
-
-  if (!g_str_equal (element->element_name, "child"))
-    return;
-
-  for (l = element->children; l; l = l->next)
-    {
-      Element *elt = l->data;
-
-      if (g_str_equal (elt->element_name, "packing"))
-        {
-          for (ll = elt->children; ll; ll = ll->next)
-            {
-              Element *elt2 = ll->data;
-
-              if (g_str_equal (elt2->element_name, "property") &&
-                  has_attribute (elt2, "name", "pack-type"))
-                {
-                  pack_type = elt2;
-                  elt->children = g_list_remove (elt->children, pack_type);
-                  if (elt->children == NULL)
-                    {
-                      element->children = g_list_remove (element->children, elt);
-                      free_element (elt);
-                    }
-                  break;
-                }
-            }
-        }
-
-      if (pack_type)
-        break;
-    }
-
-  if (pack_type)
-    {
-      char **attnames = g_new0 (char *, g_strv_length (element->attribute_names) + 2);
-      char **attvalues = g_new0 (char *, g_strv_length (element->attribute_names) + 2);
-      int i;
-
-      for (i = 0; element->attribute_names[i]; i++)
-        {
-          attnames[i] = g_strdup (element->attribute_names[i]);
-          attvalues[i] = g_strdup (element->attribute_values[i]);
-        }
-
-      attnames[i] = g_strdup ("type");
-      attvalues[i] = g_strdup (pack_type->data);
-
-      g_strfreev (element->attribute_names);
-      g_strfreev (element->attribute_values);
-
-      element->attribute_names = attnames;
-      element->attribute_values = attvalues;
-
-      free_element (pack_type);
-    }
-}
-
-static void
-rewrite_pack_type (Element *element,
-                   MyParserData *data)
-{
-  GList *l;
-
-  for (l = element->children; l; l = l->next)
-    {
-      Element *elt = l->data;
-      if (g_str_equal (elt->element_name, "child"))
-        rewrite_pack_type_child (elt, data);
-    }
-}
-
-static void
-rewrite_paned_child (Element *element,
-                     MyParserData *data,
-                     Element *child,
-                     const char *suffix)
-{
-  Element *resize = NULL;
-  Element *shrink = NULL;
-  GList *l, *ll;
-
-  for (l = child->children; l; l = l->next)
-    {
-      Element *elt = l->data;
-
-      if (g_str_equal (elt->element_name, "packing"))
-        {
-          for (ll = elt->children; ll; ll = ll->next)
-            {
-              Element *elt2 = ll->data;
-
-              if (g_str_equal (elt2->element_name, "property") &&
-                  has_attribute (elt2, "name", "resize"))
-                resize = elt2;
-              else if (g_str_equal (elt2->element_name, "property") &&
-                  has_attribute (elt2, "name", "shrink"))
-                shrink = elt2;
-            }
-          if (resize)
-            elt->children = g_list_remove (elt->children, resize);
-          if (shrink)
-            elt->children = g_list_remove (elt->children, shrink);
-          if (elt->children == NULL)
-            {
-              child->children = g_list_remove (child->children, elt);
-              free_element (elt);
-            }
-        }
-
-      if (resize || shrink)
-        break;
-    }
-
-  if (resize)
-    {
-      Element *elt;
-
-      elt = g_new0 (Element, 1);
-      elt->parent = element;
-      elt->element_name = g_strdup ("property");
-      elt->attribute_names = g_new0 (char *, 2);
-      elt->attribute_names[0] = g_strdup ("name");
-      elt->attribute_values = g_new0 (char *, 2);
-      elt->attribute_values[0] = g_strconcat ("resize-", suffix, NULL);
-      elt->data = g_strdup (resize->data);
-
-      element->children = g_list_prepend (element->children, elt);
-
-      free_element (resize);
-    }
-
-  if (shrink)
-    {
-      Element *elt;
-
-      elt = g_new0 (Element, 1);
-      elt->parent = element;
-      elt->element_name = g_strdup ("property");
-      elt->attribute_names = g_new0 (char *, 2);
-      elt->attribute_names[0] = g_strdup ("name");
-      elt->attribute_values = g_new0 (char *, 2);
-      elt->attribute_values[0] = g_strconcat ("shrink-", suffix, NULL);
-      elt->data = g_strdup (shrink->data);
-
-      element->children = g_list_prepend (element->children, elt);
-
-      free_element (shrink);
-    }
-}
-
-static void
-rewrite_paned (Element *element,
-               MyParserData *data)
-{
-  Element *child1 = NULL;
-  Element *child2 = NULL;
-  GList *l;
-
-  for (l = element->children; l; l = l->next)
-    {
-      Element *elt = l->data;
-
-      if (g_str_equal (elt->element_name, "child"))
-        {
-          if (child1 == NULL)
-            child1 = elt;
-          else if (child2 == NULL)
-            child2 = elt;
-          else
-            break;
-        }
-    }
-
-  if (child1)
-    rewrite_paned_child (element, data, child1, "start-child");
-
-  if (child2)
-    rewrite_paned_child (element, data, child2, "end-child");
-}
-
-static void
-rewrite_dialog (Element *element,
-               MyParserData *data)
-{
-  Element *content_area = NULL;
-  Element *vbox = NULL;
-  Element *action_area = NULL;
-  GList *l;
-
-  for (l = element->children; l; l = l->next)
-    {
-      Element *elt = l->data;
-
-      if (g_str_equal (elt->element_name, "child") &&
-          g_strcmp0 (get_attribute_value (elt, "internal-child"), "vbox") == 0)
-        {
-          content_area = elt;
-          break;
-        }
-    }
-
-  if (!content_area || !content_area->children)
-    return;
-
-  vbox = content_area->children->data;
-
-  for (l = vbox->children; l; l = l->next)
-    {
-      Element *elt = l->data;
-
-      if (g_str_equal (elt->element_name, "child") &&
-          g_strcmp0 (get_attribute_value (elt, "internal-child"), "action_area") == 0)
-        {
-          action_area = elt;
-          break;
-        }
-    }
-
-  if (!action_area)
-    return;
-
-  set_attribute_value (content_area, "internal-child", "content_area");
-  vbox->children = g_list_remove (vbox->children, action_area);
-  action_area->parent = element;
-  element->children = g_list_append (element->children, action_area);
-
-  for (l = action_area->children; l; l = l->next)
-    {
-      Element *elt = l->data;
-
-      if (g_str_equal (elt->element_name, "packing"))
-        {
-          action_area->children = g_list_remove (action_area->children, elt);
-          free_element (elt);
-          break;
-        }
-    }
-
-}
-
-static void
-rewrite_grid_layout_prop (Element *element,
-                          const char *attr_name,
-                          const char *old_value,
-                          const char *new_value)
-{
-  if (g_str_equal (element->element_name, "property"))
-    {
-      char *canonical_name;
-
-      canonical_name = g_strdup (old_value);
-      g_strdelimit (canonical_name, "_", '-');
-
-      if (has_attribute (element, attr_name, old_value) ||
-          has_attribute (element, attr_name, canonical_name))
-        {
-          set_attribute_value (element, attr_name, new_value);
-        }
-
-      g_free (canonical_name);
-    }
-}
-
-static void
-rewrite_grid_layout (Element *element,
-                     MyParserData *data)
-{
-  struct _Prop {
-    const char *attr_name;
-    const char *old_value;
-    const char *new_value;
-  } props[] = {
-    { "name", "left_attach", "column", },
-    { "name", "top_attach", "row", },
-    { "name", "width", "column-span", },
-    { "name", "height", "row-span", },
-  };
-  GList *l, *ll;
-
-  for (l = element->children; l; l = l->next)
-    {
-      Element *child = l->data;
-
-      if (g_str_equal (child->element_name, "child"))
-        {
-          Element *object = NULL;
-          Element *packing = NULL;
-
-          for (ll = child->children; ll; ll = ll->next)
-            {
-              Element *elt2 = ll->data;
-
-              if (g_str_equal (elt2->element_name, "object"))
-                object = elt2;
-
-              if (g_str_equal (elt2->element_name, "packing"))
-                packing = elt2;
-            }
-
-          if (object && packing)
-            {
-              int i;
-
-              child->children = g_list_remove (child->children, packing);
-
-              g_free (packing->element_name);
-              packing->element_name = g_strdup ("layout");
-
-              packing->parent = object;
-              object->children = g_list_append (object->children, packing);
-
-              for (ll = packing->children; ll; ll = ll->next)
-                {
-                  Element *elt = ll->data;
-
-                  for (i = 0; i < G_N_ELEMENTS (props); i++)
-                    rewrite_grid_layout_prop (elt,
-                                              props[i].attr_name,
-                                              props[i].old_value,
-                                              props[i].new_value);
-                }
-            }
-        }
-    }
-}
-
-static Element *
-add_element (Element    *parent,
-             const char *element_name)
-{
-  Element *child;
-
-  child = g_new0 (Element, 1);
-  child->parent = parent;
-  child->element_name = g_strdup (element_name);
-  child->attribute_names = g_new0 (char *, 1);
-  child->attribute_values = g_new0 (char *, 1);
-  parent->children = g_list_prepend (parent->children, child);
-
-  return child;
-}
-
-static Element *
-write_box_prop (Element *element,
-                Element *parent,
-                const char *name,
-                const char *value)
-{
-
-  if (element)
-    g_free (element->data);
-  else
-    {
-      element = add_element (parent, "property");
-      set_attribute_value (element, "name", name);
-    }
-  element->data = g_strdup (value);
-
-  return element;
-}
-
-static void
-rewrite_box (Element *element,
-             MyParserData *data)
-{
-  GList *l, *ll;
-  GtkOrientation orientation = GTK_ORIENTATION_HORIZONTAL;
-
-  if (g_str_equal (get_class_name (element), "GtkVBox"))
-    write_box_prop (NULL, element, "orientation", "vertical");
-
-  if (!g_str_equal (get_class_name (element), "GtkBox"))
-    set_attribute_value (element, "class", "GtkBox");
-
-  for (l = element->children; l; l = l->next)
-    {
-      Element *child = l->data;
-
-      if (g_str_equal (child->element_name, "property"))
-        {
-          if (has_attribute (child, "name", "orientation"))
-            {
-              GValue value = G_VALUE_INIT;
-
-              if (gtk_builder_value_from_string_type (data->builder,
-                                                      GTK_TYPE_ORIENTATION,
-                                                      child->data,
-                                                      &value,
-                                                      NULL))
-                orientation = g_value_get_enum (&value);
-            }
-        }
-    }
-
-  for (l = element->children; l; l = l->next)
-    {
-      Element *child = l->data;
-      if (g_str_equal (child->element_name, "child"))
-        {
-          Element *object = NULL;
-          Element *packing = NULL;
-
-          for (ll = child->children; ll; ll = ll->next)
-            {
-              Element *elt2 = ll->data;
-
-              if (g_str_equal (elt2->element_name, "object"))
-                object = elt2;
-
-              if (g_str_equal (elt2->element_name, "packing"))
-                packing = elt2;
-            }
-
-          if (object && packing)
-            {
-              Element *halign = NULL;
-              Element *hexpand = NULL;
-              Element *valign = NULL;
-              Element *vexpand = NULL;
-
-              gboolean expand = FALSE;
-              gboolean fill = TRUE;
-
-              for (ll = object->children; ll; ll = ll->next)
-                {
-                  Element *elt = ll->data;
-                  if (g_str_equal (elt->element_name, "property"))
-                    {
-                      if (has_attribute (elt, "name", "halign"))
-                        halign = elt;
-                      else if (has_attribute (elt, "name", "hexpand"))
-                        hexpand = elt;
-                      else if (has_attribute (elt, "name", "valign"))
-                        valign = elt;
-                      else if (has_attribute (elt, "name", "vexpand"))
-                        vexpand = elt;
-                    }
-                }
-
-              for (ll = packing->children; ll; ll = ll->next)
-                {
-                  Element *elt = ll->data;
-
-                  if (has_attribute (elt, "name", "expand"))
-                    {
-                      GValue value = G_VALUE_INIT;
-
-                      if (gtk_builder_value_from_string_type (data->builder,
-                                                              G_TYPE_BOOLEAN,
-                                                              elt->data,
-                                                              &value,
-                                                              NULL))
-                        expand = g_value_get_boolean (&value);
-                    }
-
-                  if (has_attribute (elt, "name", "fill"))
-                    {
-                      GValue value = G_VALUE_INIT;
-
-                      if (gtk_builder_value_from_string_type (data->builder,
-                                                              G_TYPE_BOOLEAN,
-                                                              elt->data,
-                                                              &value,
-                                                              NULL))
-                        fill = g_value_get_boolean (&value);
-                    }
-                }
-
-              if (orientation == GTK_ORIENTATION_HORIZONTAL)
-                {
-                  if (expand)
-                    hexpand = write_box_prop (hexpand, object, "hexpand", "1");
-                  if (!fill)
-                    halign = write_box_prop (halign, object, "halign", "center");
-                }
-              else if (orientation == GTK_ORIENTATION_VERTICAL)
-                {
-                  if (expand)
-                    vexpand = write_box_prop (vexpand, object, "vexpand", "1");
-                  if (!fill)
-                    valign = write_box_prop (valign, object, "valign", "center");
-                }
-
-              child->children = g_list_remove (child->children, packing);
-              free_element (packing);
-            }
-        }
-    }
-}
-
-static void
-rewrite_bin_child (Element      *element,
-                   MyParserData *data)
-{
-  GList *l, *ll;
-  const char *class_name;
-  GType type;
-
-  for (l = element->children; l; l = l->next)
-    {
-      Element *child = l->data;
-      Element *object = NULL;
-
-      if (!g_str_equal (child->element_name, "child") ||
-          has_attribute (child, "type", NULL))
-        continue;
-
-      for (ll = child->children; ll; ll = ll->next)
-        {
-          Element *elem = ll->data;
-
-          if (!g_str_equal (elem->element_name, "object"))
-            continue;
-
-          class_name = get_attribute_value (elem, "class");
-          if (!class_name)
-            continue;
-
-          type = g_type_from_name (class_name);
-          if (!g_type_is_a (type, GTK_TYPE_WIDGET))
-            continue;
-
-          object = elem;
-        }
-
-      if (object)
-        {
-          g_free (child->element_name);
-          g_strfreev (child->attribute_names);
-          g_strfreev (child->attribute_values);
-          child->element_name = g_strdup ("property");
-          child->attribute_names = g_new0 (char *, 2);
-          child->attribute_names[0] = g_strdup ("name");
-          child->attribute_values = g_new0 (char *, 2);
-          child->attribute_values[0] = g_strdup ("child");
-          break;
-        }
-    }
-}
-
-static gboolean
-remove_boolean_prop (Element      *element,
-                     MyParserData *data,
-                     const char   *prop_name,
-                     gboolean     *value)
-{
-  GList *l;
-
-  for (l = element->children; l; l = l->next)
-    {
-      Element *child = l->data;
-
-      if (g_str_equal (child->element_name, "property") &&
-          has_attribute (child, "name", prop_name))
-        {
-          *value = strcmp (canonical_boolean_value (data, child->data), "1") == 0;
-          element->children = g_list_remove (element->children, child);
-          free_element (child);
-          return TRUE;
-        }
-    }
-
-  return FALSE;
-}
-
-static void
-rewrite_radio_button (Element      *element,
-                      MyParserData *data)
-{
-  gboolean draw_indicator = TRUE;
-
-  if (!remove_boolean_prop (element, data, "draw-indicator", &draw_indicator))
-    remove_boolean_prop (element, data, "draw_indicator", &draw_indicator);
-
-  if (draw_indicator)
-    set_attribute_value (element, "class", "GtkCheckButton");
-  else
-    set_attribute_value (element, "class", "GtkToggleButton");
-
-}
-
-static gboolean
-has_prop (Element      *element,
-          MyParserData *data,
-          const char   *prop_name)
-{
-  GList *l;
-
-  for (l = element->children; l; l = l->next)
-    {
-      Element *child = l->data;
-
-      if (g_str_equal (child->element_name, "property") &&
-          has_attribute (child, "name", prop_name))
-        return TRUE;
-    }
-
-  return FALSE;
-}
-
-static void
-rewrite_scale (Element      *element,
-               MyParserData *data)
-{
-  if (!has_prop (element, data, "draw-value") &&
-      !has_prop (element, data, "draw_value"))
-    {
-      Element *child;
-      child = add_element (element, "property");
-      set_attribute_value (child, "name", "draw-value");
-      child->data = g_strdup ("1");
-    }
-}
-
-static void
-rewrite_requires (Element      *element,
-                  MyParserData *data)
-{
-  if (has_attribute (element, "lib", "gtk+"))
-    {
-      set_attribute_value (element, "lib", "gtk");
-      set_attribute_value (element, "version", "4.0");
-    }
-}
-
-static void
-rewrite_overlay (Element      *element,
-                 MyParserData *data)
-{
-  GList *l, *ll;
-
-  for (l = element->children; l; l = l->next)
-    {
-      Element *child = l->data;
-
-      if (g_str_equal (child->element_name, "child"))
-        {
-          Element *object = NULL;
-          Element *packing = NULL;
-
-          for (ll = child->children; ll; ll = ll->next)
-            {
-              Element *elt2 = ll->data;
-
-              if (g_str_equal (elt2->element_name, "object"))
-                object = elt2;
-
-              if (g_str_equal (elt2->element_name, "packing"))
-                packing = elt2;
-            }
-
-          if (object && packing)
-            {
-              child->children = g_list_remove (child->children, packing);
-
-              for (ll = packing->children; ll; ll = ll->next)
-                {
-                  Element *elt2 = ll->data;
-
-                  if (g_str_equal (elt2->element_name, "property") &&
-                      (has_attribute (elt2, "name", "pass-through") ||
-                       has_attribute (elt2, "name", "pass_through")))
-                    {
-                      const char *b = canonical_boolean_value (data, elt2->data);
-                      if (g_str_equal (b, "1"))
-                        {
-                          Element *new_prop;
-
-                          new_prop = add_element (object, "property");
-                          set_attribute_value (new_prop, "name", "can-target");
-                          new_prop->data = g_strdup ("0");
-                        }
-                      break;
-                    }
-                }
-
-              free_element (packing);
-            }
-        }
-    }
-}
-
-static void
-rewrite_toolbar (Element      *element,
-                 MyParserData *data)
-{
-  GList *l, *ll;
-  Element *style = NULL;
-
-  set_attribute_value (element, "class", "GtkBox");
-
-  for (l = element->children; l; l = l->next)
-    {
-      Element *child = l->data;
-
-      if (g_str_equal (child->element_name, "property") &&
-          (has_attribute (child, "name", "toolbar_style") ||
-           has_attribute (child, "name", "toolbar-style")))
-        {
-          element->children = g_list_remove (element->children, child);
-          free_element (child);
-          break;
-        }
-    }
-
-  for (l = element->children; l; l = l->next)
-    {
-      Element *child = l->data;
-      Element *object = NULL;
-      Element *packing = NULL;
-
-      if (g_str_equal (child->element_name, "style"))
-        style = child;
-
-      if (!g_str_equal (child->element_name, "child"))
-        continue;
-
-      for (ll = child->children; ll; ll = ll->next)
-        {
-          Element *elt2 = ll->data;
-
-          if (g_str_equal (elt2->element_name, "object"))
-            object = elt2;
-
-          if (g_str_equal (elt2->element_name, "packing"))
-            packing = elt2;
-        }
-
-      if (object)
-        {
-          const char *class_name;
-
-          class_name = get_class_name (object);
-
-          if (g_str_equal (class_name, "GtkToolButton"))
-            {
-              set_attribute_value (object, "class", "GtkButton");
-            }
-          else if (g_str_equal (class_name, "GtkToggleToolButton") ||
-                   g_str_equal (class_name, "GtkRadioToolButton"))
-            {
-              set_attribute_value (object, "class", "GtkToggleButton");
-            }
-          else if (g_str_equal (class_name, "GtkSeparatorToolItem"))
-            {
-              Element *prop;
-
-              set_attribute_value (object, "class", "GtkSeparator");
-              prop = add_element (object, "property");
-              set_attribute_value (prop, "name", "orientation");
-              prop->data = g_strdup ("vertical");
-            }
-        }
-
-      if (packing)
-        child->children = g_list_remove (child->children, packing);
-    }
-
-  if (!style)
-    style = add_element (element, "style");
-
-  set_attribute_value (add_element (style, "class"), "name", "toolbar");
-}
-
-static void
-rewrite_fixed (Element      *element,
-               MyParserData *data)
-{
-  GList *l, *ll;
-
-  for (l = element->children; l; l = l->next)
-    {
-      Element *child = l->data;
-
-      if (g_str_equal (child->element_name, "child"))
-        {
-          Element *object = NULL;
-          Element *packing = NULL;
-
-          for (ll = child->children; ll; ll = ll->next)
-            {
-              Element *elt2 = ll->data;
-
-              if (g_str_equal (elt2->element_name, "object"))
-                object = elt2;
-
-              if (g_str_equal (elt2->element_name, "packing"))
-                packing = elt2;
-            }
-
-          if (object && packing)
-            {
-              int x = 0;
-              int y = 0;
-              Element *layout;
-              Element *new_prop;
-              GskTransform *transform;
-
-              for (ll = packing->children; ll; ll = ll->next)
-                {
-                  Element *elt2 = ll->data;
-                  GValue value = G_VALUE_INIT;
-
-                  if (has_attribute (elt2, "name", "x"))
-                    {
-                      if (gtk_builder_value_from_string_type (data->builder, G_TYPE_INT, elt2->data, &value, NULL))
-                        x = g_value_get_int (&value);
-                    }
-                  else if (has_attribute (elt2, "name", "y"))
-                    {
-                      if (gtk_builder_value_from_string_type (data->builder, G_TYPE_INT, elt2->data, &value, NULL))
-                        y = g_value_get_int (&value);
-                    }
-                }
-
-              child->children = g_list_remove (child->children, packing);
-              free_element (packing);
-
-              layout = add_element (object, "layout");
-              new_prop = add_element (layout, "property");
-              set_attribute_value (new_prop, "name", "transform");
-
-              transform = gsk_transform_translate (NULL, &GRAPHENE_POINT_INIT (x, y));
-              new_prop->data = gsk_transform_to_string (transform);
-              gsk_transform_unref (transform);
-            }
-        }
-    }
-}
-
-/* returns TRUE to remove the element from the parent */
-static gboolean
-simplify_element (Element      *element,
-                  MyParserData *data)
-{
-  GList *l;
-
-  if (!is_pcdata_element (element))
-    g_clear_pointer (&element->data, g_free);
-  else if (g_str_equal (element->element_name, "property") &&
-           property_is_boolean (element, data))
-    {
-      const char *b = canonical_boolean_value (data, element->data);
-      g_free (element->data);
-      element->data = g_strdup (b);
-    }
-
-  l = element->children;
-  while (l)
-    {
-      GList *next = l->next;
-      Element *child = l->data;
-      if (simplify_element (child, data))
-        {
-          element->children = g_list_remove (element->children, child);
-          free_element (child);
-        }
-      l = next;
-    }
-
-  if (is_container_element (element) && element->children == NULL)
-    return TRUE;
-
-  if (g_str_equal (element->element_name, "property") &&
-      property_can_be_omitted (element, data))
-    return TRUE;
-
-  if (g_str_equal (element->element_name, "binding"))
-    {
-      const char *property_name = get_attribute_value (element, "name");
-      const char *class_name = get_class_name (element);
-      if (!get_property_pspec (data, class_name, property_name, PROP_KIND_OBJECT))
-        warn_missing_property (element, data, class_name, property_name, PROP_KIND_OBJECT);
-    }
-
-  return FALSE;
-}
-
-static void
-simplify_tree (MyParserData *data)
-{
-  simplify_element (data->root, data);
-}
-
-static gboolean
-rewrite_element (Element      *element,
-                 MyParserData *data)
-{
-  GList *l;
-
-  l = element->children;
-  while (l)
-    {
-      GList *next = l->next;
-      Element *child = l->data;
-      if (rewrite_element (child, data))
-        {
-          element->children = g_list_remove (element->children, child);
-          free_element (child);
-        }
-      l = next;
-    }
-
-  if (element_is_object_or_template (element) &&
-      g_str_equal (get_class_name (element), "GtkStack"))
-    rewrite_stack (element, data);
-
-  if (element_is_object_or_template (element) &&
-      g_str_equal (get_class_name (element), "GtkAssistant"))
-    rewrite_assistant (element, data);
-
-  if (element_is_object_or_template (element) &&
-      g_str_equal (get_class_name (element), "GtkNotebook"))
-    rewrite_notebook (element, data);
-
-  if (element_is_object_or_template (element) &&
-      (g_str_equal (get_class_name (element), "GtkActionBar") ||
-       g_str_equal (get_class_name (element), "GtkHeaderBar")))
-    rewrite_pack_type (element, data);
-
-  if (element_is_object_or_template (element) &&
-      g_str_equal (get_class_name (element), "GtkToolbar"))
-    rewrite_toolbar (element, data);
-
-  if (element_is_object_or_template (element) &&
-      g_str_equal (get_class_name (element), "GtkPaned"))
-    rewrite_paned (element, data);
-
-  if (element_is_object_or_template (element) &&
-      g_str_equal (get_class_name (element), "GtkDialog"))
-    rewrite_dialog (element, data);
-
-  if (element_is_object_or_template (element) &&
-      g_str_equal (get_class_name (element), "GtkOverlay"))
-    rewrite_overlay (element, data);
-
-  if (element_is_object_or_template (element) &&
-      g_str_equal (get_class_name (element), "GtkGrid"))
-    rewrite_grid_layout (element, data);
-
-  if (element_is_object_or_template (element) &&
-      (g_str_equal (get_class_name (element), "GtkHBox") ||
-       g_str_equal (get_class_name (element), "GtkVBox") ||
-       g_str_equal (get_class_name (element), "GtkBox")))
-    rewrite_box (element, data);
-
-  if (element_is_object_or_template (element) &&
-      g_str_equal (get_class_name (element), "GtkFixed"))
-    rewrite_fixed (element, data);
-
-  if (element_is_object_or_template (element) &&
-      (g_str_equal (get_class_name (element), "GtkAspectFrame") ||
-       g_str_equal (get_class_name (element), "GtkComboBox") ||
-       g_str_equal (get_class_name (element), "GtkComboBoxText") ||
-       g_str_equal (get_class_name (element), "GtkFlowBoxChild") ||
-       g_str_equal (get_class_name (element), "GtkFrame") ||
-       g_str_equal (get_class_name (element), "GtkListBoxRow") ||
-       g_str_equal (get_class_name (element), "GtkOverlay") ||
-       g_str_equal (get_class_name (element), "GtkPopover") ||
-       g_str_equal (get_class_name (element), "GtkPopoverMenu") ||
-       g_str_equal (get_class_name (element), "GtkRevealer") ||
-       g_str_equal (get_class_name (element), "GtkScrolledWindow") ||
-       g_str_equal (get_class_name (element), "GtkSearchBar") ||
-       g_str_equal (get_class_name (element), "GtkViewport") ||
-       g_str_equal (get_class_name (element), "GtkWindow")))
-    rewrite_bin_child (element, data);
-
-  if (element_is_object_or_template (element) &&
-      g_str_equal (get_class_name (element), "GtkRadioButton"))
-    rewrite_radio_button (element, data);
-
-  if (element_is_object_or_template (element) &&
-      g_str_equal (get_class_name (element), "GtkScale"))
-    rewrite_scale (element, data);
-
-  if (g_str_equal (element->element_name, "property"))
-    maybe_rename_property (element, data);
-
-  if (g_str_equal (element->element_name, "property") &&
-      property_has_been_removed (element, data))
-    return TRUE;
-
-  if (g_str_equal (element->element_name, "requires"))
-    rewrite_requires (element, data);
-
-  return FALSE;
-}
-
-static void
-rewrite_tree (MyParserData *data)
-{
-  rewrite_element (data->root, data);
-}
-
-/* For properties which have changed their default
- * value between 3 and 4, we make sure that their
- * old default value is present in the tree before
- * simplifying it.
- *
- * So far, this is just GtkWidget::visible,
- * changing its default from 0 to 1.
- */
-static void
-add_old_default_properties (Element      *element,
-                            MyParserData *data)
-{
-  const char *class_name;
-  GType type;
-
-  if (!g_str_equal (element->element_name, "object"))
-    return;
-
-  class_name = get_class_name (element);
-  type = g_type_from_name (class_name);
-  if (g_type_is_a (type, GTK_TYPE_WIDGET))
-    {
-      GList *l;
-      gboolean has_visible = FALSE;
-
-      for (l = element->children; l; l = l->next)
-        {
-          Element *prop = l->data;
-          const char *name = get_attribute_value (prop, "name");
-
-          if (g_str_equal (prop->element_name, "property") &&
-              g_str_equal (name, "visible"))
-            has_visible = TRUE;
-        }
-
-      if (!has_visible)
-        {
-          Element *new_prop;
-
-          new_prop = add_element (element, "property");
-          set_attribute_value (new_prop, "name", "visible");
-          new_prop->data = g_strdup ("0");
-        }
-    }
-}
-
-static void
-enhance_element (Element      *element,
-                 MyParserData *data)
-{
-  GList *l;
-
-  if (strcmp (element->element_name, "requires") == 0 &&
-      has_attribute (element, "lib", "gtk+"))
-    {
-      data->has_gtk_requires = TRUE;
-    }
-
-  add_old_default_properties (element, data);
-
-  for (l = element->children; l; l = l->next)
-    {
-      Element *child = l->data;
-      enhance_element (child, data);
-    }
-
-  if (element == data->root && !data->has_gtk_requires)
-    {
-      Element *requires = add_element (element, "requires");
-      set_attribute_value (requires, "lib", "gtk+");
-      set_attribute_value (requires, "version", "3.0");
-    }
-}
-
-static void
-enhance_tree (MyParserData *data)
-{
-  enhance_element (data->root, data);
-}
-
-static void
-dump_element (Element *element,
-              FILE    *output,
-              int      indent)
-{
-  g_fprintf (output, "%*s<%s", indent, "", element->element_name);
-  if (element->attribute_names[0])
-    {
-      int i;
-      for (i = 0; element->attribute_names[i]; i++)
-        {
-          char *escaped = g_markup_escape_text (element->attribute_values[i], -1);
-          g_fprintf (output, " %s=\"%s\"", element->attribute_names[i], escaped);
-          g_free (escaped);
-        }
-    }
-  if (element->children || element->data)
-    {
-      g_fprintf (output, ">");
-
-      if (element->children)
-        {
-          GList *l;
-
-          g_fprintf (output, "\n");
-          for (l = element->children; l; l = l->next)
-            {
-              Element *child = l->data;
-              dump_element (child, output, indent + 2);
-            }
-          g_fprintf (output, "%*s", indent, "");
-        }
-      else
-        {
-          if (is_cdata_property (element))
-            {
-              g_fprintf (output, "<![CDATA[");
-              g_fprintf (output, "%s", element->data);
-              g_fprintf (output, "]]>");
-            }
-          else
-            {
-              char *escaped = g_markup_escape_text (element->data, -1);
-              g_fprintf (output, "%s", escaped);
-              g_free (escaped);
-            }
-        }
-      g_fprintf (output, "</%s>\n", element->element_name);
-    }
-  else
-    g_fprintf (output, "/>\n");
-}
-
-static void
-write_xml_declaration (FILE *output)
-{
-  g_fprintf (output, "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n");
-}
-
-static void
-dump_tree (MyParserData *data)
-{
-  write_xml_declaration (data->output);
-  dump_element (data->root, data->output, 0);
-}
-
-static gboolean
-simplify_file (const char *filename,
-               gboolean    replace,
-               gboolean    convert3to4)
-{
-  GMarkupParseContext *context;
-  char *buffer;
-  MyParserData data;
-  GError *error = NULL;
-
-  data.input_filename = filename;
-  data.output_filename = NULL;
-  data.convert3to4 = convert3to4;
-  data.has_gtk_requires = FALSE;
-
-  if (replace)
-    {
-      int fd;
-      fd = g_file_open_tmp ("gtk-builder-tool-XXXXXX", &data.output_filename, NULL);
-      data.output = fdopen (fd, "w");
-    }
-  else
-    {
-      data.output = stdout;
-    }
-
-  if (!g_file_get_contents (filename, &buffer, NULL, &error))
-    {
-      g_printerr (_("Can’t load “%s”: %s\n"), filename, error->message);
-      return FALSE;
-    }
-
-  data.root = NULL;
-  data.current = NULL;
-  data.value = g_string_new ("");
-
-  context = g_markup_parse_context_new (&parser, G_MARKUP_TREAT_CDATA_AS_TEXT, &data, NULL);
-  if (!g_markup_parse_context_parse (context, buffer, -1, &error))
-    {
-      g_printerr (_("Can’t parse “%s”: %s\n"), filename, error->message);
-      return FALSE;
-    }
-
-  data.builder = gtk_builder_new ();
-
-  if (data.convert3to4)
-    {
-      enhance_tree (&data);
-      rewrite_tree (&data);
-    }
-  simplify_tree (&data);
-
-  dump_tree (&data);
-
-  fclose (data.output);
-
-  if (data.output_filename)
-    {
-      char *content;
-      gsize length;
-
-      if (!g_file_get_contents (data.output_filename, &content, &length, &error))
-        {
-          g_printerr (_("Failed to read “%s”: %s\n"), data.output_filename, error->message);
-          return FALSE;
-        }
-
-      if (!g_file_set_contents (data.input_filename, content, length, &error))
-        {
-          g_printerr (_("Failed to write %s: “%s”\n"), data.input_filename, error->message);
-          return FALSE;
-        }
-    }
-
-  return TRUE;
-}
-
-void
-do_simplify (int          *argc,
-             const char ***argv)
-{
-  gboolean replace = FALSE;
-  gboolean convert3to4 = FALSE;
-  char **filenames = NULL;
-  GOptionContext *ctx;
-  const GOptionEntry entries[] = {
-    { "replace", 0, 0, G_OPTION_ARG_NONE, &replace, NULL, NULL },
-    { "3to4", 0, 0, G_OPTION_ARG_NONE, &convert3to4, NULL, NULL },
-    { G_OPTION_REMAINING, 0, 0, G_OPTION_ARG_FILENAME_ARRAY, &filenames, NULL, NULL },
-    { NULL, }
-  };
-  GError *error = NULL;
-  int i;
-
-  ctx = g_option_context_new (NULL);
-  g_option_context_set_help_enabled (ctx, FALSE);
-  g_option_context_add_main_entries (ctx, entries, NULL);
-
-  if (!g_option_context_parse (ctx, argc, (char ***)argv, &error))
-    {
-      g_printerr ("%s\n", error->message);
-      g_error_free (error);
-      exit (1);
-    }
-
-  g_option_context_free (ctx);
-
-  if (filenames == NULL)
-    {
-      g_printerr (_("No .ui file specified\n"));
-      exit (1);
-    }
-
-  if (g_strv_length (filenames) > 1 && !replace)
-    {
-      g_printerr (_("Can only simplify a single .ui file without --replace\n"));
-      exit (1);
-    }
-
-  for (i = 0; filenames[i]; i++)
-    {
-      if (!simplify_file (filenames[i], replace, convert3to4))
-        exit (1);
-    }
-}
diff --git a/gtk/tools/gtk-builder-tool-validate.c b/gtk/tools/gtk-builder-tool-validate.c
deleted file mode 100644 (file)
index 8551231..0000000
+++ /dev/null
@@ -1,160 +0,0 @@
-/*  Copyright 2015 Red Hat, Inc.
- *
- * GTK+ is free software; you can redistribute it and/or modify it
- * under the terms of the GNU Lesser General Public License as
- * published by the Free Software Foundation; either version 2 of the
- * License, or (at your option) any later version.
- *
- * GLib is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with GTK+; see the file COPYING.  If not,
- * see <http://www.gnu.org/licenses/>.
- *
- * Author: Matthias Clasen
- */
-
-#include <stdlib.h>
-#include <string.h>
-#include <errno.h>
-
-#include <glib/gi18n.h>
-#include <glib/gprintf.h>
-#include <glib/gstdio.h>
-#include <gtk/gtk.h>
-#include "gtkbuilderprivate.h"
-#include "gtk-builder-tool.h"
-
-static GType
-make_fake_type (const char *type_name,
-                const char *parent_name)
-{
-  GType parent_type;
-  GTypeQuery query;
-
-  parent_type = g_type_from_name (parent_name);
-  if (parent_type == G_TYPE_INVALID)
-    {
-      g_printerr ("Failed to lookup template parent type %s\n", parent_name);
-      exit (1);
-    }
-
-  g_type_query (parent_type, &query);
-  return g_type_register_static_simple (parent_type,
-                                        type_name,
-                                        query.class_size,
-                                        NULL,
-                                        query.instance_size,
-                                        NULL,
-                                        0);
-}
-
-static void
-do_validate_template (const char *filename,
-                      const char *type_name,
-                      const char *parent_name)
-{
-  GType template_type;
-  GObject *object;
-  GtkBuilder *builder;
-  GError *error = NULL;
-  int ret;
-
-  /* Only make a fake type if it doesn't exist yet.
-   * This lets us e.g. validate the GtkFileChooserWidget template.
-   */
-  template_type = g_type_from_name (type_name);
-  if (template_type == G_TYPE_INVALID)
-    template_type = make_fake_type (type_name, parent_name);
-
-  object = g_object_new (template_type, NULL);
-  if (!object)
-    {
-      g_printerr ("Failed to create an instance of the template type %s\n", type_name);
-      exit (1);
-    }
-
-  builder = gtk_builder_new ();
-  ret = gtk_builder_extend_with_template (builder, object , template_type, " ", 1, &error);
-  if (ret)
-    ret = gtk_builder_add_from_file (builder, filename, &error);
-  g_object_unref (builder);
-
-  if (ret == 0)
-    {
-      g_printerr ("%s\n", error->message);
-      exit (1);
-    }
-}
-
-static gboolean
-parse_template_error (const char   *message,
-                      char        **class_name,
-                      char        **parent_name)
-{
-  char *p;
-
-  p = strstr (message, "(class '");
-  if (p)
-    {
-      *class_name = g_strdup (p + strlen ("(class '"));
-      p = strstr (*class_name, "'");
-      if (p)
-        *p = '\0';
-    }
-  p = strstr (message, ", parent '");
-  if (p)
-    {
-      *parent_name = g_strdup (p + strlen (", parent '"));
-      p = strstr (*parent_name, "'");
-      if (p)
-        *p = '\0';
-    }
-
-  return TRUE;
-}
-
-static gboolean
-validate_file (const char *filename)
-{
-  GtkBuilder *builder;
-  GError *error = NULL;
-  int ret;
-  char *class_name = NULL;
-  char *parent_name = NULL;
-
-  builder = gtk_builder_new ();
-  ret = gtk_builder_add_from_file (builder, filename, &error);
-  g_object_unref (builder);
-
-  if (ret == 0)
-    {
-      if (g_error_matches (error, GTK_BUILDER_ERROR, GTK_BUILDER_ERROR_UNHANDLED_TAG)  &&
-          parse_template_error (error->message, &class_name, &parent_name))
-        {
-          do_validate_template (filename, class_name, parent_name);
-        }
-      else
-        {
-          g_printerr ("%s\n", error->message);
-          return FALSE;
-        }
-    }
-
-  return TRUE;
-}
-
-void
-do_validate (int *argc, const char ***argv)
-{
-  int i;
-
-  for (i = 1; i < *argc; i++)
-    {
-      if (!validate_file ((*argv)[i]))
-        exit (1);
-    }
-}
diff --git a/gtk/tools/gtk-builder-tool.c b/gtk/tools/gtk-builder-tool.c
deleted file mode 100644 (file)
index bc89207..0000000
+++ /dev/null
@@ -1,141 +0,0 @@
-/*  Copyright 2015 Red Hat, Inc.
- *
- * GTK+ is free software; you can redistribute it and/or modify it
- * under the terms of the GNU Lesser General Public License as
- * published by the Free Software Foundation; either version 2 of the
- * License, or (at your option) any later version.
- *
- * GLib is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with GTK+; see the file COPYING.  If not,
- * see <http://www.gnu.org/licenses/>.
- *
- * Author: Matthias Clasen
- */
-
-#include <stdlib.h>
-#include <string.h>
-#include <errno.h>
-
-#include <glib/gi18n.h>
-#include <glib/gprintf.h>
-#include <glib/gstdio.h>
-#include <gtk/gtk.h>
-#include "gtkbuilderprivate.h"
-#include "gtk-builder-tool.h"
-
-static void G_GNUC_NORETURN
-usage (void)
-{
-  g_print (_("Usage:\n"
-             "  gtk-builder-tool [COMMAND] [OPTION…] FILE\n"
-             "\n"
-             "Commands:\n"
-             "  validate     Validate the file\n"
-             "  simplify     Simplify the file\n"
-             "  enumerate    List all named objects\n"
-             "  preview      Preview the file\n"
-             "\n"
-             "Simplify Options:\n"
-             "  --replace    Replace the file\n"
-             "  --3to4       Convert from GTK 3 to GTK 4\n"
-             "\n"
-             "Preview Options:\n"
-             "  --id=ID      Preview only the named object\n"
-             "  --css=FILE   Use style from CSS file\n"
-             "\n"
-             "Perform various tasks on GtkBuilder .ui files.\n"));
-  exit (1);
-}
-
-/* A simplified version of g_log_writer_default_would_drop(), to avoid
- * bumping up the required version of GLib to 2.68
- */
-static gboolean
-would_drop (GLogLevelFlags  level,
-            const char     *domain)
-{
-  return (level & (G_LOG_LEVEL_ERROR |
-                   G_LOG_LEVEL_CRITICAL |
-                   G_LOG_LEVEL_WARNING)) == 0;
-}
-
-static GLogWriterOutput
-log_writer_func (GLogLevelFlags   level,
-                 const GLogField *fields,
-                 gsize            n_fields,
-                 gpointer         user_data)
-{
-  gsize i;
-  const char *domain = NULL;
-  const char *message = NULL;
-
-  for (i = 0; i < n_fields; i++)
-    {
-      if (g_strcmp0 (fields[i].key, "GLIB_DOMAIN") == 0)
-        domain = fields[i].value;
-      else if (g_strcmp0 (fields[i].key, "MESSAGE") == 0)
-        message = fields[i].value;
-    }
-
-  if (message != NULL && !would_drop (level, domain))
-    {
-      const char *prefix;
-      switch (level & G_LOG_LEVEL_MASK)
-        {
-        case G_LOG_LEVEL_ERROR:
-          prefix = "ERROR";
-          break;
-        case G_LOG_LEVEL_CRITICAL:
-          prefix = "CRITICAL";
-          break;
-        case G_LOG_LEVEL_WARNING:
-          prefix = "WARNING";
-          break;
-        default:
-          prefix = "INFO";
-          break;
-        }
-      g_printerr ("%s-%s: %s\n", domain, prefix, message);
-    }
-
-  return G_LOG_WRITER_HANDLED;
-}
-
-int
-main (int argc, const char *argv[])
-{
-  g_set_prgname ("gtk-builder-tool");
-
-  g_log_set_writer_func (log_writer_func, NULL, NULL);
-
-  gtk_init ();
-
-  gtk_test_register_all_types ();
-
-  if (argc < 3)
-    usage ();
-
-  if (strcmp (argv[2], "--help") == 0)
-    usage ();
-
-  argv++;
-  argc--;
-
-  if (strcmp (argv[0], "validate") == 0)
-    do_validate (&argc, &argv);
-  else if (strcmp (argv[0], "simplify") == 0)
-    do_simplify (&argc, &argv);
-  else if (strcmp (argv[0], "enumerate") == 0)
-    do_enumerate (&argc, &argv);
-  else if (strcmp (argv[0], "preview") == 0)
-    do_preview (&argc, &argv);
-  else
-    usage ();
-
-  return 0;
-}
diff --git a/gtk/tools/gtk-builder-tool.h b/gtk/tools/gtk-builder-tool.h
deleted file mode 100644 (file)
index 3d895d8..0000000
+++ /dev/null
@@ -1,10 +0,0 @@
-
-#ifndef __GTK_BUILDER_TOOL_H__
-#define __GTK_BUILDER_TOOL_H__
-
-void do_simplify  (int *argc, const char ***argv);
-void do_validate  (int *argc, const char ***argv);
-void do_enumerate (int *argc, const char ***argv);
-void do_preview   (int *argc, const char ***argv);
-
-#endif
diff --git a/gtk/tools/gtk-launch.c b/gtk/tools/gtk-launch.c
deleted file mode 100644 (file)
index 9305095..0000000
+++ /dev/null
@@ -1,200 +0,0 @@
-/* GTK - The GIMP Toolkit
- *
- * Copyright (C) 2012 Red Hat, Inc.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Library General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Library General Public License for more details.
- *
- * You should have received a copy of the GNU Library General Public
- * License along with this library. If not, see <http://www.gnu.org/licenses/>.
- *
- * Author: Tomas Bzatek <tbzatek@redhat.com>
- */
-
-#include <config.h>
-
-#include <stdio.h>
-#include <unistd.h>
-#include <locale.h>
-#include <errno.h>
-
-#include <glib.h>
-#include <glib/gi18n.h>
-#include <gio/gio.h>
-#if defined(HAVE_GIO_UNIX) && !defined(__APPLE__)
-#include <gio/gdesktopappinfo.h>
-#endif
-#include <gtk.h>
-
-static gboolean show_version;
-static char **args = NULL;
-
-static GOptionEntry entries[] = {
-  { "version", 0, 0, G_OPTION_ARG_NONE, &show_version, N_("Show program version"), NULL },
-  { G_OPTION_REMAINING, 0, 0, G_OPTION_ARG_FILENAME_ARRAY, &args, NULL, NULL },
-  { NULL}
-};
-
-int
-main (int argc, char *argv[])
-{
-  GError *error = NULL;
-  GOptionContext *context = NULL;
-  char *summary;
-  char *app_name;
-#ifdef G_OS_UNIX
-  char *desktop_file_name;
-  char *bus_name = NULL;
-#endif
-  GAppInfo *info = NULL;
-  GAppLaunchContext *launch_context;
-  GList *l;
-  GFile *f;
-
-  setlocale (LC_ALL, "");
-
-#ifdef ENABLE_NLS
-  bindtextdomain (GETTEXT_PACKAGE, GTK_LOCALEDIR);
-  textdomain (GETTEXT_PACKAGE);
-#ifdef HAVE_BIND_TEXTDOMAIN_CODESET
-  bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
-#endif
-#endif
-
-  context =
-  /* Translators: this message will appear immediately after the */
-  /* usage string - Usage: COMMAND [OPTION…] <THIS_MESSAGE>    */
-    g_option_context_new (_("APPLICATION [URI…] — launch an APPLICATION"));
-
-  /* Translators: this message will appear after the usage string */
-  /* and before the list of options.                              */
-  summary = _("Launch an application (specified by its desktop file name),\n"
-              "optionally passing one or more URIs as arguments.");
-  g_option_context_set_summary (context, summary);
-  g_option_context_add_main_entries (context, entries, GETTEXT_PACKAGE);
-  g_option_context_parse (context, &argc, &argv, &error);
-
-  g_option_context_free (context);
-
-  if (error != NULL)
-    {
-      g_printerr (_("Error parsing commandline options: %s\n"), error->message);
-      g_printerr ("\n");
-      g_printerr (_("Try “%s --help” for more information."), g_get_prgname ());
-      g_printerr ("\n");
-      g_error_free (error);
-      return 1;
-    }
-
-  if (show_version)
-    {
-      g_print ("%d.%d.%d\n",
-               gtk_get_major_version (),
-               gtk_get_minor_version (),
-               gtk_get_micro_version ());
-      return 0;
-    }
-
-  if (!args)
-    {
-      /* Translators: the %s is the program name. This error message */
-      /* means the user is calling gtk-launch without any argument.  */
-      g_printerr (_("%s: missing application name"), g_get_prgname ());
-      g_printerr ("\n");
-      g_printerr (_("Try “%s --help” for more information."), g_get_prgname ());
-      g_printerr ("\n");
-      return 1;
-    }
-
-
-  gtk_init ();
-
-  app_name = *args;
-#if defined(HAVE_GIO_UNIX) && !defined(__APPLE__)
-  bus_name = g_strdup (app_name);
-  if (g_str_has_suffix (app_name, ".desktop"))
-    {
-      desktop_file_name = g_strdup (app_name);
-      bus_name[strlen (bus_name) - strlen(".desktop")] = '\0';
-    }
-  else
-    {
-      desktop_file_name = g_strconcat (app_name, ".desktop", NULL);
-    }
-
-  if (!g_dbus_is_name (bus_name))
-    g_clear_pointer (&bus_name, g_free);
-  info = G_APP_INFO (g_desktop_app_info_new (desktop_file_name));
-  g_free (desktop_file_name);
-#else
-#warning Please add support for creating AppInfo from id for your OS
-  g_printerr (_("Creating AppInfo from id not supported on non unix operating systems"));
-#endif
-  args++;
-
-  if (!info)
-    {
-      /* Translators: the first %s is the program name, the second one */
-      /* is the application name.                                      */
-      g_printerr (_("%s: no such application %s"),
-                  g_get_prgname (), app_name);
-      g_printerr ("\n");
-      return 2;
-    }
-
-  l = NULL;
-  for (; *args; args++)
-    {
-      f = g_file_new_for_commandline_arg (*args);
-      l = g_list_append (l, f);
-    }
-
-  launch_context = (GAppLaunchContext*) gdk_display_get_app_launch_context (gdk_display_get_default ());
-  if (!g_app_info_launch (info, l, launch_context, &error))
-    {
-       /* Translators: the first %s is the program name, the second one  */
-       /* is the error message.                                          */
-       g_printerr (_("%s: error launching application: %s\n"),
-                   g_get_prgname (), error->message);
-       return 3;
-    }
-  g_object_unref (info);
-  g_object_unref (launch_context);
-
-#ifdef G_OS_UNIX
-  if (bus_name != NULL)
-    {
-      GDBusConnection *connection;
-      char *object_path, *p;
-
-      connection = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, NULL);
-
-      object_path = g_strdup_printf ("/%s", bus_name);
-      for (p = object_path; *p != '\0'; p++)
-          if (*p == '.')
-              *p = '/';
-
-      if (connection)
-        g_dbus_connection_call_sync (connection,
-                                     bus_name,
-                                     object_path,
-                                     "org.freedesktop.DBus.Peer",
-                                     "Ping",
-                                     NULL, NULL,
-                                     G_DBUS_CALL_FLAGS_NONE, -1, NULL, NULL);
-      g_clear_pointer (&object_path, g_free);
-      g_clear_object (&connection);
-      g_clear_pointer (&bus_name, g_free);
-    }
-#endif
-  g_list_free_full (l, g_object_unref);
-
-  return 0;
-}
diff --git a/gtk/tools/gtk-query-settings.c b/gtk/tools/gtk-query-settings.c
deleted file mode 100644 (file)
index fbaaa33..0000000
+++ /dev/null
@@ -1,95 +0,0 @@
-/*  Copyright 2015 Timm Bäder
- *
- * GTK+ is free software; you can redistribute it and/or modify it
- * under the terms of the GNU Lesser General Public License as
- * published by the Free Software Foundation; either version 2 of the
- * License, or (at your option) any later version.
- *
- * GLib is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with GTK+; see the file COPYING.  If not,
- * see <http://www.gnu.org/licenses/>.
- */
-
-#include <glib.h>
-#include <gtk/gtk.h>
-#include <string.h>
-
-
-int
-main (int argc, char **argv)
-{
-  GtkSettings  *settings;
-  GParamSpec  **props;
-  guint         n_properties;
-  guint         i;
-  int           max_prop_name_length = 0;
-  char         *pattern = NULL;
-
-  gtk_init ();
-
-  if (argc > 1)
-    pattern = argv[1];
-
-  settings = gtk_settings_get_default ();
-  props = g_object_class_list_properties (G_OBJECT_GET_CLASS (settings), &n_properties);
-
-  for (i = 0; i < n_properties; i ++)
-    {
-      int len = strlen (props[i]->name);
-
-      if (len > max_prop_name_length)
-        max_prop_name_length = len;
-    }
-
-
-  for (i = 0; i < n_properties; i ++)
-    {
-      GValue      value = {0};
-      GParamSpec *prop = props[i];
-      char       *value_str;
-      int         spacing = max_prop_name_length - strlen (prop->name) + 1;
-      gboolean    deprecated;
-
-      if (pattern && !g_strrstr (prop->name, pattern))
-        continue;
-
-      g_value_init (&value, prop->value_type);
-      g_object_get_property (G_OBJECT (settings), prop->name, &value);
-      deprecated = prop->flags & G_PARAM_DEPRECATED;
-
-      if (G_VALUE_HOLDS_ENUM (&value))
-        {
-          GEnumClass *enum_class = G_PARAM_SPEC_ENUM (prop)->enum_class;
-          GEnumValue *enum_value = g_enum_get_value (enum_class, g_value_get_enum (&value));
-
-          value_str = g_strdup (enum_value->value_name);
-        }
-      else
-        {
-          value_str = g_strdup_value_contents (&value);
-        }
-
-      if (deprecated)
-        {
-          printf ("!");
-          spacing --;
-        }
-
-      for (; spacing >= 0; spacing --)
-        printf (" ");
-
-      printf ("%s: %s\n", prop->name, value_str);
-
-      g_free (value_str);
-      g_value_unset (&value);
-    }
-
-  g_free (props);
-
-  return 0;
-}
diff --git a/gtk/tools/gtk4builder.its b/gtk/tools/gtk4builder.its
deleted file mode 100644 (file)
index 689ef0d..0000000
+++ /dev/null
@@ -1,23 +0,0 @@
-<?xml version="1.0"?>
-<its:rules xmlns:its="http://www.w3.org/2005/11/its"
-           xmlns:gt="https://www.gnu.org/s/gettext/ns/its/extensions/1.0"
-           version="2.0">
-  <its:translateRule selector="/interface" translate="no"/>
-  <its:translateRule selector="/interface//*[@translatable = 'yes']"
-                     translate="yes"/>
-
-  <!-- The 'comment' attribute should be extracted as a translator comment.  -->
-  <its:locNoteRule selector="/interface//*[@comments]"
-                   locNotePointer="@comments"
-                   locNoteType="alert"/>
-  <gt:escapeRule selector="/interface//@comments" escape="no"/>
-
-  <!-- The 'context' attribute should be extracted as msgctxt.  -->
-  <gt:contextRule selector="/interface//*[@context]" contextPointer="@context"/>
-
-  <its:preserveSpaceRule selector="/interface" space="preserve"/>
-
-  <!-- Extracted strings are consumed by the library and are never
-       merged back; we don't want to escape special characters.  -->
-  <gt:escapeRule selector="/interface" escape="no"/>
-</its:rules>
diff --git a/gtk/tools/gtk4builder.loc b/gtk/tools/gtk4builder.loc
deleted file mode 100644 (file)
index 5d77e48..0000000
+++ /dev/null
@@ -1,6 +0,0 @@
-<?xml version="1.0"?>
-<locatingRules>
-  <locatingRule name="GtkBuilder" pattern="*.ui">
-    <documentRule localName="interface" target="gtkbuilder.its"/>
-  </locatingRule>
-</locatingRules>
diff --git a/gtk/tools/gtk4builder.rng b/gtk/tools/gtk4builder.rng
deleted file mode 100644 (file)
index 1250515..0000000
+++ /dev/null
@@ -1,343 +0,0 @@
-<?xml version="1.0"?>
-<grammar xmlns="http://relaxng.org/ns/structure/1.0" ns="">
-  <start>
-    <element name="interface">
-      <optional>
-        <attribute name="domain">
-          <text/>
-        </attribute>
-      </optional>
-      <zeroOrMore>
-        <choice>
-          <ref name="requires"/>
-          <ref name="object"/>
-          <ref name="template"/>
-          <ref name="menu"/>
-        </choice>
-      </zeroOrMore>
-    </element>
-  </start>
-  <define name="requires">
-    <element name="requires">
-      <attribute name="lib">
-        <text/>
-      </attribute>
-      <attribute name="version">
-        <text/>
-      </attribute>
-    </element>
-  </define>
-  <define name="object">
-    <element name="object">
-      <optional>
-        <attribute name="id">
-          <data type="ID" datatypeLibrary="http://www.w3.org/2001/XMLSchema-datatypes"/>
-        </attribute>
-      </optional>
-      <attribute name="class">
-        <text/>
-      </attribute>
-      <optional>
-        <attribute name="type-func">
-          <text/>
-        </attribute>
-      </optional>
-      <optional>
-        <attribute name="constructor">
-          <text/>
-        </attribute>
-      </optional>
-      <zeroOrMore>
-        <choice>
-          <ref name="property"/>
-          <ref name="signal"/>
-          <ref name="child"/>
-          <ref name="ANY"/>
-        </choice>
-      </zeroOrMore>
-    </element>
-  </define>
-  <define name="template">
-    <element name="template">
-      <attribute name="class">
-        <text/>
-      </attribute>
-      <attribute name="parent">
-        <text/>
-      </attribute>
-      <zeroOrMore>
-        <choice>
-          <ref name="property"/>
-          <ref name="signal"/>
-          <ref name="child"/>
-          <ref name="ANY"/>
-        </choice>
-      </zeroOrMore>
-    </element>
-  </define>
-  <define name="property">
-    <element name="property">
-      <attribute name="name">
-        <text/>
-      </attribute>
-      <optional>
-        <attribute name="translatable">
-          <choice>
-            <value>yes</value>
-            <value>no</value>
-          </choice>
-        </attribute>
-      </optional>
-      <optional>
-        <attribute name="comments">
-          <text/>
-        </attribute>
-      </optional>
-      <optional>
-        <attribute name="context">
-          <text/>
-        </attribute>
-      </optional>
-      <optional>
-        <group>
-          <attribute name="bind-source">
-            <text/>
-          </attribute>
-          <optional>
-            <attribute name="bind-property">
-              <text/>
-            </attribute>
-          </optional>
-          <optional>
-            <attribute name="bind-flags">
-              <text/>
-            </attribute>
-          </optional>
-        </group>
-      </optional>
-      <optional>
-        <text/>
-      </optional>
-    </element>
-  </define>
-  <define name="signal">
-    <element name="signal">
-      <attribute name="name">
-        <text/>
-      </attribute>
-      <attribute name="handler">
-        <text/>
-      </attribute>
-      <optional>
-        <attribute name="after">
-          <text/>
-        </attribute>
-      </optional>
-      <optional>
-        <attribute name="swapped">
-          <text/>
-        </attribute>
-      </optional>
-      <optional>
-        <attribute name="object">
-          <text/>
-        </attribute>
-      </optional>
-      <optional>
-        <attribute name="last_modification_time">
-          <text/>
-        </attribute>
-      </optional>
-      <empty/>
-    </element>
-  </define>
-  <define name="child">
-    <element name="child">
-      <optional>
-        <attribute name="type">
-          <text/>
-        </attribute>
-      </optional>
-      <optional>
-        <attribute name="internal-child">
-          <text/>
-        </attribute>
-      </optional>
-      <zeroOrMore>
-        <choice>
-          <ref name="object"/>
-          <ref name="ANY"/>
-        </choice>
-      </zeroOrMore>
-    </element>
-  </define>
-  <define name="menu">
-    <element name="menu">
-      <attribute name="id">
-        <data type="ID" datatypeLibrary="http://www.w3.org/2001/XMLSchema-datatypes"/>
-      </attribute>
-      <optional>
-        <attribute name="domain">
-          <text/>
-        </attribute>
-      </optional>
-      <zeroOrMore>
-        <choice>
-          <ref name="item"/>
-          <ref name="submenu"/>
-          <ref name="section"/>
-        </choice>
-      </zeroOrMore>
-    </element>
-  </define>
-  <define name="item">
-    <element name="item">
-      <optional>
-        <attribute name="id">
-          <data type="ID" datatypeLibrary="http://www.w3.org/2001/XMLSchema-datatypes"/>
-        </attribute>
-      </optional>
-      <zeroOrMore>
-        <choice>
-          <ref name="attribute_"/>
-          <ref name="link"/>
-        </choice>
-      </zeroOrMore>
-    </element>
-  </define>
-  <define name="attribute_">
-    <element name="attribute">
-      <attribute name="name">
-        <text/>
-      </attribute>
-      <optional>
-        <attribute name="type">
-          <text/>
-        </attribute>
-      </optional>
-      <optional>
-        <attribute name="translatable">
-          <choice>
-            <value>yes</value>
-            <value>no</value>
-          </choice>
-        </attribute>
-      </optional>
-      <optional>
-        <attribute name="context">
-          <text/>
-        </attribute>
-      </optional>
-      <optional>
-        <attribute name="comments">
-          <text/>
-        </attribute>
-      </optional>
-      <optional>
-        <text/>
-      </optional>
-    </element>
-  </define>
-  <define name="link">
-    <element name="link">
-      <optional>
-        <attribute name="id">
-          <data type="ID" datatypeLibrary="http://www.w3.org/2001/XMLSchema-datatypes"/>
-        </attribute>
-      </optional>
-      <attribute name="name">
-        <text/>
-      </attribute>
-      <zeroOrMore>
-        <ref name="item"/>
-      </zeroOrMore>
-    </element>
-  </define>
-  <define name="submenu">
-    <element name="submenu">
-      <optional>
-        <attribute name="id">
-          <data type="ID" datatypeLibrary="http://www.w3.org/2001/XMLSchema-datatypes"/>
-        </attribute>
-      </optional>
-      <zeroOrMore>
-        <choice>
-          <ref name="attribute_"/>
-          <ref name="item"/>
-          <ref name="submenu"/>
-          <ref name="section"/>
-        </choice>
-      </zeroOrMore>
-    </element>
-  </define>
-  <define name="section">
-    <element name="section">
-      <optional>
-        <attribute name="id">
-          <data type="ID" datatypeLibrary="http://www.w3.org/2001/XMLSchema-datatypes"/>
-        </attribute>
-      </optional>
-      <zeroOrMore>
-        <choice>
-          <ref name="attribute_"/>
-          <ref name="item"/>
-          <ref name="submenu"/>
-          <ref name="section"/>
-        </choice>
-      </zeroOrMore>
-    </element>
-  </define>
-  <define name="ANY">
-    <element>
-      <anyName>
-        <except>
-          <name>interface</name>
-          <name>requires</name>
-          <name>object</name>
-          <name>property</name>
-          <name>signal</name>
-          <name>child</name>
-          <name>menu</name>
-          <name>item</name>
-          <name>attribute</name>
-          <name>link</name>
-          <name>submenu</name>
-          <name>section</name>
-        </except>
-      </anyName>
-      <zeroOrMore>
-        <attribute>
-          <anyName/>
-          <text/>
-        </attribute>
-      </zeroOrMore>
-      <interleave>
-        <zeroOrMore>
-          <ref name="ALL"/>
-        </zeroOrMore>
-        <optional>
-          <text/>
-        </optional>
-      </interleave>
-    </element>
-  </define>
-  <define name="ALL">
-    <element>
-      <anyName/>
-      <zeroOrMore>
-        <attribute>
-          <anyName/>
-          <text/>
-        </attribute>
-      </zeroOrMore>
-      <interleave>
-        <zeroOrMore>
-          <ref name="ALL"/>
-        </zeroOrMore>
-        <optional>
-          <text/>
-        </optional>
-      </interleave>
-    </element>
-  </define>
-</grammar>
diff --git a/gtk/tools/gtkiconcachevalidator.c b/gtk/tools/gtkiconcachevalidator.c
deleted file mode 100644 (file)
index 7cec4d4..0000000
+++ /dev/null
@@ -1,403 +0,0 @@
-/* gtkiconcachevalidator.c
- * Copyright (C) 2007 Red Hat, Inc
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Library General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Library General Public License for more details.
- *
- * You should have received a copy of the GNU Library General Public
- * License along with this library. If not, see <http://www.gnu.org/licenses/>.
- */
-#include "config.h"
-#include "gtkiconcachevalidatorprivate.h"
-
-#include <glib.h>
-#include <gdk-pixbuf/gdk-pixdata.h>
-
-
-#define VERBOSE(x)
-
-#define check(name,condition) \
-  if (!(condition)) \
-    { \
-      VERBOSE(g_message ("bad %s", (name))); \
-      return FALSE; \
-    }
-
-static inline gboolean
-get_uint16 (CacheInfo *info,
-            guint32    offset,
-            guint16   *value)
-{
-  if (offset < info->cache_size)
-    {
-      *value = GUINT16_FROM_BE(*(guint16*)(info->cache + offset));
-      return TRUE;
-    }
-  else
-    {
-      *value = 0;
-      return FALSE;
-    }
-}
-
-static inline gboolean
-get_uint32 (CacheInfo *info,
-            guint32    offset,
-            guint32   *value)
-{
-  if (offset < info->cache_size)
-    {
-      *value = GUINT32_FROM_BE(*(guint32*)(info->cache + offset));
-      return TRUE;
-    }
-  else
-    {
-      *value = 0;
-      return FALSE;
-    }
-}
-
-static gboolean
-check_version (CacheInfo *info)
-{
-  guint16 major, minor;
-
-  check ("major version", get_uint16 (info, 0, &major) && major == 1);
-  check ("minor version", get_uint16 (info, 2, &minor) && minor == 0);
-
-  return TRUE;
-}
-
-static gboolean
-check_string (CacheInfo *info,
-              guint32    offset)
-{
-  check ("string offset", offset < info->cache_size);
-
-  if (info->flags & CHECK_STRINGS)
-    {
-      int i;
-      char c;
-
-      /* assume no string is longer than 1k */
-      for (i = 0; i < 1024; i++)
-        {
-          check ("string offset", offset + i < info->cache_size)
-          c = *(info->cache + offset + i);
-          if (c == '\0')
-            break;
-          check ("string content", g_ascii_isgraph (c));
-        }
-      check ("string length", i < 1024);
-    }
-
-  return TRUE;
-}
-
-static gboolean
-check_string_utf8 (CacheInfo *info,
-                   guint32    offset)
-{
-  check ("string offset", offset < info->cache_size);
-
-  if (info->flags & CHECK_STRINGS)
-    {
-      int i;
-      char c;
-
-      /* assume no string is longer than 1k */
-      for (i = 0; i < 1024; i++)
-        {
-          check ("string offset", offset + i < info->cache_size)
-            c = *(info->cache + offset + i);
-          if (c == '\0')
-            break;
-        }
-      check ("string length", i < 1024);
-      check ("string utf8 data", g_utf8_validate((char *)(info->cache + offset), -1, NULL));
-    }
-
-  return TRUE;
-}
-
-static gboolean
-check_directory_list (CacheInfo *info,
-                      guint32    offset)
-{
-  guint32 directory_offset;
-  int i;
-
-  check ("offset, directory list", get_uint32 (info, offset, &info->n_directories));
-
-  for (i = 0; i < info->n_directories; i++)
-    {
-      check ("offset, directory", get_uint32 (info, offset + 4 + 4 * i, &directory_offset));
-      if (!check_string (info, directory_offset))
-        return FALSE;
-    }
-
-  return TRUE;
-}
-
-static gboolean
-check_pixel_data (CacheInfo *info,
-                  guint32    offset)
-{
-  guint32 type;
-  guint32 length;
-
-  check ("offset, pixel data type", get_uint32 (info, offset, &type));
-  check ("offset, pixel data length", get_uint32 (info, offset + 4, &length));
-
-  check ("pixel data type", type == 0);
-  check ("pixel data length", offset + 8 + length < info->cache_size);
-
-  if (info->flags & CHECK_PIXBUFS)
-    {
-      GdkPixdata data;
-
-G_GNUC_BEGIN_IGNORE_DEPRECATIONS;
-      check ("pixel data", gdk_pixdata_deserialize (&data, length,
-                                                    (const guint8*)info->cache + offset + 8,
-                                                    NULL));
-G_GNUC_END_IGNORE_DEPRECATIONS;
-    }
-
-  return TRUE;
-}
-
-static gboolean
-check_embedded_rect (CacheInfo *info,
-                     guint32    offset)
-{
-  check ("embedded rect", offset + 4 < info->cache_size);
-
-  return TRUE;
-}
-
-static gboolean
-check_attach_point_list (CacheInfo *info,
-                         guint32    offset)
-{
-  guint32 n_attach_points;
-
-  check ("offset, attach point list", get_uint32 (info, offset, &n_attach_points));
-  check ("attach points", offset + 4 + 4 * n_attach_points < info->cache_size);
-
-  return TRUE;
-}
-
-static gboolean
-check_display_name_list (CacheInfo *info,
-                         guint32    offset)
-{
-  guint32 n_display_names, ofs;
-  int i;
-
-  check ("offset, display name list",
-         get_uint32 (info, offset, &n_display_names));
-  for (i = 0; i < n_display_names; i++)
-    {
-      get_uint32(info, offset + 4 + 8 * i, &ofs);
-      if (!check_string (info, ofs))
-        return FALSE;
-      get_uint32(info, offset + 4 + 8 * i + 4, &ofs);
-      if (!check_string_utf8 (info, ofs))
-        return FALSE;
-    }
-
-  return TRUE;
-}
-
-static gboolean
-check_meta_data (CacheInfo *info,
-                 guint32    offset)
-{
-  guint32 embedded_rect_offset;
-  guint32 attach_point_list_offset;
-  guint32 display_name_list_offset;
-
-  check ("offset, embedded rect",
-         get_uint32 (info, offset, &embedded_rect_offset));
-  check ("offset, attach point list",
-         get_uint32 (info, offset + 4, &attach_point_list_offset));
-  check ("offset, display name list",
-         get_uint32 (info, offset + 8, &display_name_list_offset));
-
-  if (embedded_rect_offset != 0)
-    {
-      if (!check_embedded_rect (info, embedded_rect_offset))
-        return FALSE;
-    }
-
-  if (attach_point_list_offset != 0)
-    {
-      if (!check_attach_point_list (info, attach_point_list_offset))
-        return FALSE;
-    }
-
-  if (display_name_list_offset != 0)
-    {
-      if (!check_display_name_list (info, display_name_list_offset))
-        return FALSE;
-    }
-
-  return TRUE;
-}
-
-static gboolean
-check_image_data (CacheInfo *info,
-                  guint32    offset)
-{
-  guint32 pixel_data_offset;
-  guint32 meta_data_offset;
-
-  check ("offset, pixel data", get_uint32 (info, offset, &pixel_data_offset));
-  check ("offset, meta data", get_uint32 (info, offset + 4, &meta_data_offset));
-
-  if (pixel_data_offset != 0)
-    {
-      if (!check_pixel_data (info, pixel_data_offset))
-        return FALSE;
-    }
-  if (meta_data_offset != 0)
-    {
-      if (!check_meta_data (info, meta_data_offset))
-        return FALSE;
-    }
-
-  return TRUE;
-}
-
-static gboolean
-check_image (CacheInfo *info,
-             guint32    offset)
-{
-  guint16 index;
-  guint16 flags;
-  guint32 image_data_offset;
-
-  check ("offset, image index", get_uint16 (info, offset, &index));
-  check ("offset, image flags", get_uint16 (info, offset + 2, &flags));        
-  check ("offset, image data offset",
-         get_uint32 (info, offset + 4, &image_data_offset));
-
-  check ("image index", index < info->n_directories);
-  check ("image flags", flags < 16);
-
-  if (image_data_offset != 0)
-    {
-      if (!check_image_data (info, image_data_offset))
-        return FALSE;
-    }
-
-  return TRUE;
-}
-
-static gboolean
-check_image_list (CacheInfo *info,
-                  guint32    offset)
-{
-  guint32 n_images;
-  int i;
-
-  check ("offset, image list", get_uint32 (info, offset, &n_images));
-
-  for (i = 0; i < n_images; i++)
-    {
-      if (!check_image (info, offset + 4 + 8 * i))
-        return FALSE;
-    }
-
-  return TRUE;
-}
-
-static gboolean
-check_icon (CacheInfo *info,
-            guint32    offset)
-{
-  guint32 chain_offset;
-  guint32 name_offset;
-  guint32 image_list_offset;
-
-  check ("offset, icon chain", get_uint32 (info, offset, &chain_offset));
-  check ("offset, icon name", get_uint32 (info, offset + 4, &name_offset));
-  check ("offset, icon image list", get_uint32 (info, offset + 8,
-         &image_list_offset));
-
-  if (!check_string (info, name_offset))
-    return FALSE;
-  if (!check_image_list (info, image_list_offset))
-    return FALSE;
-  if (chain_offset != 0xffffffff)
-    {
-      if (!check_icon (info, chain_offset))
-        return FALSE;
-    }
-
-  return TRUE;
-}
-
-static gboolean
-check_hash (CacheInfo *info,
-            guint32    offset)
-{
-  guint32 n_buckets, icon_offset;
-  int i;
-
-  check ("offset, hash size", get_uint32 (info, offset, &n_buckets));
-
-  for (i = 0; i < n_buckets; i++)
-    {
-      check ("offset, hash chain",
-             get_uint32 (info, offset + 4 + 4 * i, &icon_offset));
-      if (icon_offset != 0xffffffff)
-        {
-          if (!check_icon (info, icon_offset))
-            return FALSE;
-        }
-    }
-
-  return TRUE;
-}
-
-/**
- * gtk_icon_cache_validate:
- * @info: a CacheInfo structure
- *
- * Validates the icon cache passed in the @cache and
- * @cache_size fields of the @info structure. The
- * validator checks that offsets specified in the
- * cache do not point outside the mapped area, that
- * strings look reasonable, and that pixbufs can
- * be deserialized. The amount of validation can
- * be controlled with the @flags field.
- *
- * Returns: %TRUE if the cache is valid
- */
-gboolean
-gtk_icon_cache_validate (CacheInfo *info)
-{
-  guint32 hash_offset;
-  guint32 directory_list_offset;
-
-  if (!check_version (info))
-    return FALSE;
-  check ("header, hash offset", get_uint32 (info, 4, &hash_offset));
-  check ("header, directory list offset", get_uint32 (info, 8, &directory_list_offset));
-  if (!check_directory_list (info, directory_list_offset))
-    return FALSE;
-
-  if (!check_hash (info, hash_offset))
-    return FALSE;
-
-  return TRUE;
-}
-
diff --git a/gtk/tools/meson.build b/gtk/tools/meson.build
deleted file mode 100644 (file)
index 8f7a62d..0000000
+++ /dev/null
@@ -1,40 +0,0 @@
-# Installed tools
-gtk_tools = [
-  ['gtk4-query-settings', ['gtk-query-settings.c']],
-  ['gtk4-builder-tool', ['gtk-builder-tool.c',
-                         'gtk-builder-tool-simplify.c',
-                         'gtk-builder-tool-validate.c',
-                         'gtk-builder-tool-enumerate.c',
-                         'gtk-builder-tool-preview.c']],
-  ['gtk4-update-icon-cache', ['updateiconcache.c', 'gtkiconcachevalidator.c']],
-  ['gtk4-encode-symbolic-svg', ['encodesymbolic.c', 'gdkpixbufutils.c']],
-]
-
-if os_unix
-  gtk_tools += [['gtk4-launch', ['gtk-launch.c']]]
-endif
-
-foreach tool: gtk_tools
-  tool_name = tool.get(0)
-  tool_srcs = tool.get(1)
-
-  exe = executable(tool_name,
-    sources: tool_srcs,
-    include_directories: [confinc],
-    c_args: common_cflags,
-    dependencies: libgtk_dep,
-    install: true,
-  )
-
-  set_variable(tool_name.underscorify(), exe) # used in testsuites
-endforeach
-
-# Data to install
-install_data('gtk4builder.rng', install_dir: gtk_datadir / 'gtk-4.0')
-
-install_data([
-    'gtk4builder.loc',
-    'gtk4builder.its',
-  ],
-  install_dir: gtk_datadir / 'gettext/its',
-)
diff --git a/gtk/tools/updateiconcache.c b/gtk/tools/updateiconcache.c
deleted file mode 100644 (file)
index 4973215..0000000
+++ /dev/null
@@ -1,1771 +0,0 @@
-/* updateiconcache.c
- * Copyright (C) 2004  Anders Carlsson <andersca@gnome.org>
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Library General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Library General Public License for more details.
- *
- * You should have received a copy of the GNU Library General Public
- * License along with this library. If not, see <http://www.gnu.org/licenses/>.
- */
-
-#include "config.h"
-
-#include <locale.h>
-#include <stdlib.h>
-#include <stdio.h>
-#include <string.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <fcntl.h>
-#ifdef HAVE_UNISTD_H
-#include <unistd.h>
-#endif
-#include <errno.h>
-#ifdef _MSC_VER
-#include <io.h>
-#include <sys/utime.h>
-#else
-#include <utime.h>
-#endif
-
-#include <glib.h>
-#include <glib/gstdio.h>
-#include <gdk-pixbuf/gdk-pixdata.h>
-#include <glib/gi18n.h>
-#include "gtkiconcachevalidatorprivate.h"
-
-static gboolean force_update = FALSE;
-static gboolean ignore_theme_index = FALSE;
-static gboolean quiet = FALSE;
-static gboolean index_only = TRUE;
-static gboolean validate = FALSE;
-static char *var_name = (char *) "-";
-
-#define CACHE_NAME "icon-theme.cache"
-
-#define HAS_SUFFIX_XPM (1 << 0)
-#define HAS_SUFFIX_SVG (1 << 1)
-#define HAS_SUFFIX_PNG (1 << 2)
-#define HAS_ICON_FILE  (1 << 3)
-
-#define MAJOR_VERSION 1
-#define MINOR_VERSION 0
-#define HASH_OFFSET 12
-
-#define ALIGN_VALUE(this, boundary) \
-  (( ((unsigned long)(this)) + (((unsigned long)(boundary)) -1)) & (~(((unsigned long)(boundary))-1)))
-
-#ifdef HAVE_FTW_H
-
-#include <ftw.h>
-
-static GStatBuf cache_dir_stat;
-static gboolean cache_up_to_date;
-
-static int check_dir_mtime (const char        *dir,
-                            const struct stat *sb,
-                            int                tf)
-{
-  if (tf != FTW_NS && sb->st_mtime > cache_dir_stat.st_mtime)
-    {
-      cache_up_to_date = FALSE;
-      /* stop tree walk */
-      return 1;
-    }
-
-  return 0;
-}
-
-static gboolean
-is_cache_up_to_date (const char *path)
-{
-  char *cache_path;
-  int retval;
-
-  cache_path = g_build_filename (path, CACHE_NAME, NULL);
-  retval = g_stat (cache_path, &cache_dir_stat);
-  g_free (cache_path);
-
-  if (retval < 0)
-    {
-      /* Cache file not found */
-      return FALSE;
-    }
-
-  cache_up_to_date = TRUE;
-
-  ftw (path, check_dir_mtime, 20);
-
-  return cache_up_to_date;
-}
-
-#else  /* !HAVE_FTW_H */
-
-gboolean
-is_cache_up_to_date (const char *path)
-{
-  GStatBuf path_stat, cache_stat;
-  char *cache_path;
-  int retval;
-
-  retval = g_stat (path, &path_stat);
-
-  if (retval < 0)
-    {
-      /* We can't stat the path,
-       * assume we have a updated cache */
-      return TRUE;
-    }
-
-  cache_path = g_build_filename (path, CACHE_NAME, NULL);
-  retval = g_stat (cache_path, &cache_stat);
-  g_free (cache_path);
-
-  if (retval < 0)
-    {
-      /* Cache file not found */
-      return FALSE;
-    }
-
-  /* Check mtime */
-  return cache_stat.st_mtime >= path_stat.st_mtime;
-}
-
-#endif  /* !HAVE_FTW_H */
-
-static gboolean
-has_theme_index (const char *path)
-{
-  gboolean result;
-  char *index_path;
-
-  index_path = g_build_filename (path, "index.theme", NULL);
-
-  result = g_file_test (index_path, G_FILE_TEST_IS_REGULAR);
-
-  g_free (index_path);
-
-  return result;
-}
-
-
-typedef struct
-{
-  GdkPixdata pixdata;
-  gboolean has_pixdata;
-  guint32 offset;
-  guint size;
-} ImageData;
-
-typedef struct
-{
-  int has_embedded_rect;
-  int x0, y0, x1, y1;
-
-  int n_attach_points;
-  int *attach_points;
-
-  int n_display_names;
-  char **display_names;
-
-  guint32 offset;
-  int size;
-} IconData;
-
-static GHashTable *image_data_hash = NULL;
-static GHashTable *icon_data_hash = NULL;
-
-typedef struct
-{
-  int flags;
-  int dir_index;
-
-  ImageData *image_data;
-  guint pixel_data_size;
-
-  IconData *icon_data;
-  guint icon_data_size;
-} Image;
-
-
-static gboolean
-foreach_remove_func (gpointer key, gpointer value, gpointer user_data)
-{
-  Image *image = (Image *)value;
-  GHashTable *files = user_data;
-  GList *list;
-  gboolean free_key = FALSE;
-
-  if (image->flags == HAS_ICON_FILE)
-    {
-      /* just a .icon file, throw away */
-      g_free (key);
-      g_free (image);
-
-      return TRUE;
-    }
-
-  list = g_hash_table_lookup (files, key);
-  if (list)
-    free_key = TRUE;
-
-  list = g_list_prepend (list, value);
-  g_hash_table_insert (files, key, list);
-
-  if (free_key)
-    g_free (key);
-
-  return TRUE;
-}
-
-static IconData *
-load_icon_data (const char *path)
-{
-  GKeyFile *icon_file;
-  char **split;
-  gsize length;
-  char *str;
-  char *split_point;
-  int i;
-  int *ivalues;
-  GError *error = NULL;
-  char **keys;
-  gsize n_keys;
-  IconData *data;
-
-  icon_file = g_key_file_new ();
-  g_key_file_set_list_separator (icon_file, ',');
-  g_key_file_load_from_file (icon_file, path, G_KEY_FILE_KEEP_TRANSLATIONS, &error);
-  if (error)
-    {
-      g_error_free (error);
-      g_key_file_free (icon_file);
-
-      return NULL;
-    }
-
-  data = g_new0 (IconData, 1);
-
-  ivalues = g_key_file_get_integer_list (icon_file,
-                                        "Icon Data", "EmbeddedTextRectangle",
-                                        &length, NULL);
-  if (ivalues)
-    {
-      if (length == 4)
-       {
-         data->has_embedded_rect = TRUE;
-         data->x0 = ivalues[0];
-         data->y0 = ivalues[1];
-         data->x1 = ivalues[2];
-         data->y1 = ivalues[3];
-       }
-
-      g_free (ivalues);
-    }
-
-  str = g_key_file_get_string (icon_file, "Icon Data", "AttachPoints", NULL);
-  if (str)
-    {
-      split = g_strsplit (str, "|", -1);
-
-      data->n_attach_points = g_strv_length (split);
-      data->attach_points = g_new (int, 2 * data->n_attach_points);
-
-      for (i = 0; i < data->n_attach_points; ++i)
-       {
-         split_point = strchr (split[i], ',');
-         if (split_point)
-           {
-             *split_point = 0;
-             split_point++;
-             data->attach_points[2 * i] = atoi (split[i]);
-             data->attach_points[2 * i + 1] = atoi (split_point);
-           }
-       }
-
-      g_strfreev (split);
-      g_free (str);
-    }
-
-  keys = g_key_file_get_keys (icon_file, "Icon Data", &n_keys, &error);
-  data->display_names = g_new0 (char *, 2 * n_keys + 1);
-  data->n_display_names = 0;
-
-  for (i = 0; i < n_keys; i++)
-    {
-      char *lang, *name;
-
-      if (g_str_has_prefix (keys[i], "DisplayName"))
-       {
-         char *open, *close = NULL;
-
-         open = strchr (keys[i], '[');
-
-         if (open)
-           close = strchr (open, ']');
-
-         if (open && close)
-           {
-             lang = g_strndup (open + 1, close - open - 1);
-             name = g_key_file_get_locale_string (icon_file,
-                                                  "Icon Data", "DisplayName",
-                                                  lang, NULL);
-           }
-         else
-           {
-             lang = g_strdup ("C");
-             name = g_key_file_get_string (icon_file,
-                                           "Icon Data", "DisplayName",
-                                           NULL);
-           }
-
-         data->display_names[2 * data->n_display_names] = lang;
-         data->display_names[2 * data->n_display_names + 1] = name;
-         data->n_display_names++;
-       }
-    }
-
-  g_strfreev (keys);
-
-  g_key_file_free (icon_file);
-
-  /* -1 means not computed yet, the real value depends
-   * on string pool state, and will be computed
-   * later
-   */
-  data->size = -1;
-
-  return data;
-}
-
-/*
- * This function was copied from gtkfilesystemunix.c, it should
- * probably go to GLib
- */
-static void
-canonicalize_filename (char *filename)
-{
-  char *p, *q;
-  gboolean last_was_slash = FALSE;
-
-  p = filename;
-  q = filename;
-
-  while (*p)
-    {
-      if (*p == G_DIR_SEPARATOR)
-       {
-         if (!last_was_slash)
-           *q++ = G_DIR_SEPARATOR;
-
-         last_was_slash = TRUE;
-       }
-      else
-       {
-         if (last_was_slash && *p == '.')
-           {
-             if (*(p + 1) == G_DIR_SEPARATOR ||
-                 *(p + 1) == '\0')
-               {
-                 if (*(p + 1) == '\0')
-                   break;
-
-                 p += 1;
-               }
-             else if (*(p + 1) == '.' &&
-                      (*(p + 2) == G_DIR_SEPARATOR ||
-                       *(p + 2) == '\0'))
-               {
-                 if (q > filename + 1)
-                   {
-                     q--;
-                     while (q > filename + 1 &&
-                            *(q - 1) != G_DIR_SEPARATOR)
-                       q--;
-                   }
-
-                 if (*(p + 2) == '\0')
-                   break;
-
-                 p += 2;
-               }
-             else
-               {
-                 *q++ = *p;
-                 last_was_slash = FALSE;
-               }
-           }
-         else
-           {
-             *q++ = *p;
-             last_was_slash = FALSE;
-           }
-       }
-
-      p++;
-    }
-
-  if (q > filename + 1 && *(q - 1) == G_DIR_SEPARATOR)
-    q--;
-
-  *q = '\0';
-}
-
-static char *
-follow_links (const char *path)
-{
-  char *target;
-  char *d, *s;
-  char *path2 = NULL;
-
-  path2 = g_strdup (path);
-  while (g_file_test (path2, G_FILE_TEST_IS_SYMLINK))
-    {
-      target = g_file_read_link (path2, NULL);
-
-      if (target)
-       {
-         if (g_path_is_absolute (target))
-           path2 = target;
-         else
-           {
-             d = g_path_get_dirname (path2);
-             s = g_build_filename (d, target, NULL);
-             g_free (d);
-             g_free (target);
-             g_free (path2);
-             path2 = s;
-           }
-       }
-      else
-       break;
-    }
-
-  if (strcmp (path, path2) == 0)
-    {
-      g_free (path2);
-      path2 = NULL;
-    }
-
-  return path2;
-}
-
-static void
-maybe_cache_image_data (Image       *image,
-                       const char *path)
-{
-  if (!index_only && !image->image_data &&
-      (g_str_has_suffix (path, ".png") || g_str_has_suffix (path, ".xpm")))
-    {
-      GdkPixbuf *pixbuf;
-      ImageData *idata;
-      char *path2;
-
-      idata = g_hash_table_lookup (image_data_hash, path);
-      path2 = follow_links (path);
-
-      if (path2)
-       {
-         ImageData *idata2;
-
-         canonicalize_filename (path2);
-
-         idata2 = g_hash_table_lookup (image_data_hash, path2);
-
-         if (idata && idata2 && idata != idata2)
-           g_error ("different idatas found for symlinked '%s' and '%s'\n",
-                    path, path2);
-
-         if (idata && !idata2)
-           g_hash_table_insert (image_data_hash, g_strdup (path2), idata);
-
-         if (!idata && idata2)
-           {
-             g_hash_table_insert (image_data_hash, g_strdup (path), idata2);
-             idata = idata2;
-           }
-       }
-
-      if (!idata)
-       {
-         idata = g_new0 (ImageData, 1);
-         g_hash_table_insert (image_data_hash, g_strdup (path), idata);
-         if (path2)
-           g_hash_table_insert (image_data_hash, g_strdup (path2), idata);
-       }
-
-      if (!idata->has_pixdata)
-       {
-         pixbuf = gdk_pixbuf_new_from_file (path, NULL);
-
-         if (pixbuf)
-           {
-G_GNUC_BEGIN_IGNORE_DEPRECATIONS;
-             gdk_pixdata_from_pixbuf (&idata->pixdata, pixbuf, FALSE);
-G_GNUC_END_IGNORE_DEPRECATIONS;
-             idata->size = idata->pixdata.length + 8;
-             idata->has_pixdata = TRUE;
-           }
-       }
-
-      image->image_data = idata;
-
-      g_free (path2);
-    }
-}
-
-static void
-maybe_cache_icon_data (Image       *image,
-                       const char *path)
-{
-  if (g_str_has_suffix (path, ".icon"))
-    {
-      IconData *idata = NULL;
-      char *path2 = NULL;
-
-      idata = g_hash_table_lookup (icon_data_hash, path);
-      path2 = follow_links (path);
-
-      if (path2)
-       {
-         IconData *idata2;
-
-         canonicalize_filename (path2);
-
-         idata2 = g_hash_table_lookup (icon_data_hash, path2);
-
-         if (idata && idata2 && idata != idata2)
-           g_error ("different idatas found for symlinked '%s' and '%s'\n",
-                    path, path2);
-
-         if (idata && !idata2)
-           g_hash_table_insert (icon_data_hash, g_strdup (path2), idata);
-
-         if (!idata && idata2)
-           {
-             g_hash_table_insert (icon_data_hash, g_strdup (path), idata2);
-             idata = idata2;
-           }
-       }
-
-      if (!idata)
-       {
-         idata = load_icon_data (path);
-         g_hash_table_insert (icon_data_hash, g_strdup (path), idata);
-         if (path2)
-           g_hash_table_insert (icon_data_hash, g_strdup (path2), idata);
-        }
-
-      image->icon_data = idata;
-
-      g_free (path2);
-    }
-}
-
-/*
- * Finds all dir separators and replaces them with “/”.
- * This makes sure that only /-separated paths are written in cache files,
- * maintaining compatibility with theme index files that use slashes as
- * directory separators on all platforms.
- */
-static void
-replace_backslashes_with_slashes (char *path)
-{
-  size_t i;
-  if (path == NULL)
-    return;
-  for (i = 0; path[i]; i++)
-    if (G_IS_DIR_SEPARATOR (path[i]))
-      path[i] = '/';
-}
-
-static GList *
-scan_directory (const char *base_path,
-               const char *subdir,
-               GHashTable  *files,
-               GList       *directories,
-               int          depth)
-{
-  GHashTable *dir_hash;
-  GDir *dir;
-  GList *list = NULL, *iterator = NULL;
-  const char *name;
-  char *dir_path;
-  gboolean dir_added = FALSE;
-  guint dir_index = 0xffff;
-
-  dir_path = g_build_path ("/", base_path, subdir, NULL);
-
-  /* FIXME: Use the gerror */
-  dir = g_dir_open (dir_path, 0, NULL);
-
-  if (!dir)
-    return directories;
-
-  dir_hash = g_hash_table_new (g_str_hash, g_str_equal);
-
-  while ((name = g_dir_read_name (dir)))
-    {
-      list = g_list_prepend (list, g_strdup (name));
-    }
-  list = g_list_sort (list, (GCompareFunc) strcmp);
-  for (iterator = list; iterator; iterator = iterator->next)
-    {
-      name = iterator->data;
-
-      char *path;
-      gboolean retval;
-      int flags = 0;
-      Image *image;
-      char *basename, *dot;
-
-      path = g_build_filename (dir_path, name, NULL);
-
-      retval = g_file_test (path, G_FILE_TEST_IS_DIR);
-      if (retval)
-       {
-         char *subsubdir;
-
-         if (subdir)
-           subsubdir = g_build_path ("/", subdir, name, NULL);
-         else
-           subsubdir = g_strdup (name);
-         directories = scan_directory (base_path, subsubdir, files,
-                                       directories, depth + 1);
-         g_free (subsubdir);
-
-         continue;
-       }
-
-      /* ignore images in the toplevel directory */
-      if (subdir == NULL)
-        continue;
-
-      retval = g_file_test (path, G_FILE_TEST_IS_REGULAR);
-      if (retval)
-       {
-         if (g_str_has_suffix (name, ".png"))
-           flags |= HAS_SUFFIX_PNG;
-         else if (g_str_has_suffix (name, ".svg"))
-           flags |= HAS_SUFFIX_SVG;
-         else if (g_str_has_suffix (name, ".xpm"))
-           flags |= HAS_SUFFIX_XPM;
-         else if (g_str_has_suffix (name, ".icon"))
-           flags |= HAS_ICON_FILE;
-
-         if (flags == 0)
-           continue;
-
-         basename = g_strdup (name);
-         dot = strrchr (basename, '.');
-         *dot = '\0';
-
-         image = g_hash_table_lookup (dir_hash, basename);
-         if (!image)
-           {
-             if (!dir_added)
-               {
-                 dir_added = TRUE;
-                 if (subdir)
-                   {
-                     dir_index = g_list_length (directories);
-                     directories = g_list_append (directories, g_strdup (subdir));
-                   }
-                 else
-                   dir_index = 0xffff;
-               }
-
-             image = g_new0 (Image, 1);
-             image->dir_index = dir_index;
-             g_hash_table_insert (dir_hash, g_strdup (basename), image);
-           }
-
-         image->flags |= flags;
-
-         maybe_cache_image_data (image, path);
-          maybe_cache_icon_data (image, path);
-
-         g_free (basename);
-       }
-
-      g_free (path);
-    }
-
-  g_list_free_full (list, g_free);
-  g_dir_close (dir);
-
-  /* Move dir into the big file hash */
-  g_hash_table_foreach_remove (dir_hash, foreach_remove_func, files);
-
-  g_hash_table_destroy (dir_hash);
-
-  return directories;
-}
-
-typedef struct _HashNode HashNode;
-
-struct _HashNode
-{
-  HashNode *next;
-  char *name;
-  GList *image_list;
-  int offset;
-};
-
-static guint
-icon_name_hash (gconstpointer key)
-{
-  const signed char *p = key;
-  guint32 h = *p;
-
-  if (h)
-    for (p += 1; *p != '\0'; p++)
-      h = (h << 5) - h + *p;
-
-  return h;
-}
-
-typedef struct {
-  int size;
-  HashNode **nodes;
-} HashContext;
-
-static gboolean
-convert_to_hash (gpointer key, gpointer value, gpointer user_data)
-{
-  HashContext *context = user_data;
-  guint hash;
-  HashNode *node;
-
-  hash = icon_name_hash (key) % context->size;
-
-  node = g_new0 (HashNode, 1);
-  node->next = NULL;
-  node->name = key;
-  node->image_list = value;
-
-  if (context->nodes[hash] != NULL)
-    node->next = context->nodes[hash];
-
-  context->nodes[hash] = node;
-
-  return TRUE;
-}
-
-static GHashTable *string_pool = NULL;
-
-static int
-find_string (const char *n)
-{
-  return GPOINTER_TO_INT (g_hash_table_lookup (string_pool, n));
-}
-
-static void
-add_string (const char *n, int offset)
-{
-  g_hash_table_insert (string_pool, (gpointer) n, GINT_TO_POINTER (offset));
-}
-
-static gboolean
-write_string (FILE *cache, const char *n)
-{
-  char *s;
-  int i, l;
-
-  l = ALIGN_VALUE (strlen (n) + 1, 4);
-
-  s = g_malloc0 (l);
-  strcpy (s, n);
-
-  i = fwrite (s, l, 1, cache);
-
-  g_free (s);
-
-  return i == 1;
-
-}
-
-static gboolean
-write_card16 (FILE *cache, guint16 n)
-{
-  int i;
-
-  n = GUINT16_TO_BE (n);
-
-  i = fwrite ((char *)&n, 2, 1, cache);
-
-  return i == 1;
-}
-
-static gboolean
-write_card32 (FILE *cache, guint32 n)
-{
-  int i;
-
-  n = GUINT32_TO_BE (n);
-
-  i = fwrite ((char *)&n, 4, 1, cache);
-
-  return i == 1;
-}
-
-
-static gboolean
-write_image_data (FILE *cache, ImageData *image_data, int offset)
-{
-  guint8 *s;
-  guint len;
-  int i;
-  GdkPixdata *pixdata = &image_data->pixdata;
-
-  /* Type 0 is GdkPixdata */
-  if (!write_card32 (cache, 0))
-    return FALSE;
-
-G_GNUC_BEGIN_IGNORE_DEPRECATIONS;
-  s = gdk_pixdata_serialize (pixdata, &len);
-G_GNUC_END_IGNORE_DEPRECATIONS;
-
-  if (!write_card32 (cache, len))
-    {
-      g_free (s);
-      return FALSE;
-    }
-
-  i = fwrite (s, len, 1, cache);
-
-  g_free (s);
-
-  return i == 1;
-}
-
-static gboolean
-write_icon_data (FILE *cache, IconData *icon_data, int offset)
-{
-  int ofs = offset + 12;
-  int j;
-  int tmp, tmp2;
-
-  if (icon_data->has_embedded_rect)
-    {
-      if (!write_card32 (cache, ofs))
-        return FALSE;
-
-       ofs += 8;
-    }
-  else
-    {
-      if (!write_card32 (cache, 0))
-        return FALSE;
-    }
-
-  if (icon_data->n_attach_points > 0)
-    {
-      if (!write_card32 (cache, ofs))
-        return FALSE;
-
-      ofs += 4 + 4 * icon_data->n_attach_points;
-    }
-  else
-    {
-      if (!write_card32 (cache, 0))
-        return FALSE;
-    }
-
-  if (icon_data->n_display_names > 0)
-    {
-      if (!write_card32 (cache, ofs))
-       return FALSE;
-    }
-  else
-    {
-      if (!write_card32 (cache, 0))
-        return FALSE;
-    }
-
-  if (icon_data->has_embedded_rect)
-    {
-      if (!write_card16 (cache, icon_data->x0) ||
-          !write_card16 (cache, icon_data->y0) ||
-         !write_card16 (cache, icon_data->x1) ||
-         !write_card16 (cache, icon_data->y1))
-        return FALSE;
-    }
-
-  if (icon_data->n_attach_points > 0)
-    {
-      if (!write_card32 (cache, icon_data->n_attach_points))
-        return FALSE;
-
-      for (j = 0; j < 2 * icon_data->n_attach_points; j++)
-        {
-          if (!write_card16 (cache, icon_data->attach_points[j]))
-            return FALSE;
-        }
-    }
-
-  if (icon_data->n_display_names > 0)
-    {
-      if (!write_card32 (cache, icon_data->n_display_names))
-        return FALSE;
-
-      ofs += 4 + 8 * icon_data->n_display_names;
-
-      tmp = ofs;
-      for (j = 0; j < 2 * icon_data->n_display_names; j++)
-        {
-          tmp2 = find_string (icon_data->display_names[j]);
-          if (tmp2 == 0 || tmp2 == -1)
-            {
-              tmp2 = tmp;
-              tmp += ALIGN_VALUE (strlen (icon_data->display_names[j]) + 1, 4);
-              /* We're playing a little game with negative
-               * offsets here to handle duplicate strings in
-               * the array.
-               */
-              add_string (icon_data->display_names[j], -tmp2);
-            }
-          else if (tmp2 < 0)
-            {
-              tmp2 = -tmp2;
-            }
-
-          if (!write_card32 (cache, tmp2))
-            return FALSE;
-
-        }
-
-      g_assert (ofs == ftell (cache));
-      for (j = 0; j < 2 * icon_data->n_display_names; j++)
-        {
-          tmp2 = find_string (icon_data->display_names[j]);
-          g_assert (tmp2 != 0 && tmp2 != -1);
-          if (tmp2 < 0)
-            {
-              tmp2 = -tmp2;
-              g_assert (tmp2 == ftell (cache));
-              add_string (icon_data->display_names[j], tmp2);
-              if (!write_string (cache, icon_data->display_names[j]))
-                return FALSE;
-            }
-        }
-    }
-
-  return TRUE;
-}
-
-static gboolean
-write_header (FILE *cache, guint32 dir_list_offset)
-{
-  return (write_card16 (cache, MAJOR_VERSION) &&
-         write_card16 (cache, MINOR_VERSION) &&
-         write_card32 (cache, HASH_OFFSET) &&
-         write_card32 (cache, dir_list_offset));
-}
-
-static int
-get_image_meta_data_size (Image *image)
-{
-  int i;
-
-  /* The complication with storing the size in both
-   * IconData and Image is necessary since we attribute
-   * the size of the IconData only to the first Image
-   * using it (at which time it is written out in the
-   * cache). Later Images just refer to the written out
-   * IconData via the offset.
-   */
-  if (image->icon_data_size == 0)
-    {
-      if (image->icon_data && image->icon_data->size < 0)
-       {
-          IconData *data = image->icon_data;
-
-          data->size = 0;
-
-          if (data->has_embedded_rect ||
-              data->n_attach_points > 0 ||
-              data->n_display_names > 0)
-            data->size += 12;
-
-          if (data->has_embedded_rect)
-            data->size += 8;
-
-          if (data->n_attach_points > 0)
-            data->size += 4 + data->n_attach_points * 4;
-
-          if (data->n_display_names > 0)
-            {
-              data->size += 4 + 8 * data->n_display_names;
-
-              for (i = 0; data->display_names[i]; i++)
-                {
-                  int poolv;
-                  if ((poolv = find_string (data->display_names[i])) == 0)
-                    {
-                      data->size += ALIGN_VALUE (strlen (data->display_names[i]) + 1, 4);
-                      /* Adding the string to the pool with -1
-                       * to indicate that it hasn't been written out
-                       * to the cache yet. We still need it in the
-                       * pool in case the same string occurs twice
-                       * during a get_single_node_size() calculation.
-                       */
-                      add_string (data->display_names[i], -1);
-                    }
-                }
-           }
-
-         image->icon_data_size = data->size;
-         data->size = 0;
-       }
-    }
-
-  g_assert (image->icon_data_size % 4 == 0);
-
-  return image->icon_data_size;
-}
-
-static int
-get_image_pixel_data_size (Image *image)
-{
-  /* The complication with storing the size in both
-   * ImageData and Image is necessary since we attribute
-   * the size of the ImageData only to the first Image
-   * using it (at which time it is written out in the
-   * cache). Later Images just refer to the written out
-   * ImageData via the offset.
-   */
-  if (image->pixel_data_size == 0)
-    {
-      if (image->image_data &&
-         image->image_data->has_pixdata)
-       {
-         image->pixel_data_size = image->image_data->size;
-         image->image_data->size = 0;
-       }
-    }
-
-  g_assert (image->pixel_data_size % 4 == 0);
-
-  return image->pixel_data_size;
-}
-
-static int
-get_image_data_size (Image *image)
-{
-  int len;
-
-  len = 0;
-
-  len += get_image_pixel_data_size (image);
-  len += get_image_meta_data_size (image);
-
-  /* Even if len is zero, we need to reserve space to
-   * write the ImageData, unless this is an .svg without
-   * .icon, in which case both image_data and icon_data
-   * are NULL.
-   */
-  if (len > 0 || image->image_data || image->icon_data)
-    len += 8;
-
-  return len;
-}
-
-static void
-get_single_node_size (HashNode *node, int *node_size, int *image_data_size)
-{
-  GList *list;
-
-  /* Node pointers */
-  *node_size = 12;
-
-  /* Name */
-  if (find_string (node->name) == 0)
-    {
-      *node_size += ALIGN_VALUE (strlen (node->name) + 1, 4);
-      add_string (node->name, -1);
-    }
-
-  /* Image list */
-  *node_size += 4 + g_list_length (node->image_list) * 8;
-
-  /* Image data */
-  *image_data_size = 0;
-  for (list = node->image_list; list; list = list->next)
-    {
-      Image *image = list->data;
-
-      *image_data_size += get_image_data_size (image);
-    }
-}
-
-static gboolean
-write_bucket (FILE *cache, HashNode *node, int *offset)
-{
-  while (node != NULL)
-    {
-      int node_size, image_data_size;
-      int next_offset, image_data_offset;
-      int data_offset;
-      int name_offset;
-      int name_size;
-      int image_list_offset;
-      int i, len;
-      GList *list;
-
-      g_assert (*offset == ftell (cache));
-
-      node->offset = *offset;
-
-      get_single_node_size (node, &node_size, &image_data_size);
-      g_assert (node_size % 4 == 0);
-      g_assert (image_data_size % 4 == 0);
-      image_data_offset = *offset + node_size;
-      next_offset = *offset + node_size + image_data_size;
-      /* Chain offset */
-      if (node->next != NULL)
-        {
-          if (!write_card32 (cache, next_offset))
-            return FALSE;
-        }
-      else
-        {
-          if (!write_card32 (cache, 0xffffffff))
-            return FALSE;
-        }
-
-      name_size = 0;
-      name_offset = find_string (node->name);
-      if (name_offset <= 0)
-        {
-          name_offset = *offset + 12;
-          name_size = ALIGN_VALUE (strlen (node->name) + 1, 4);
-          add_string (node->name, name_offset);
-        }
-      if (!write_card32 (cache, name_offset))
-        return FALSE;
-
-      image_list_offset = *offset + 12 + name_size;
-      if (!write_card32 (cache, image_list_offset))
-        return FALSE;
-
-      /* Icon name */
-      if (name_size > 0)
-        {
-          if (!write_string (cache, node->name))
-            return FALSE;
-        }
-
-      /* Image list */
-      len = g_list_length (node->image_list);
-      if (!write_card32 (cache, len))
-        return FALSE;
-
-      list = node->image_list;
-      data_offset = image_data_offset;
-      for (i = 0; i < len; i++)
-        {
-          Image *image = list->data;
-          int image_size = get_image_data_size (image);
-
-          /* Directory index */
-          if (!write_card16 (cache, image->dir_index))
-            return FALSE;
-
-          /* Flags */
-          if (!write_card16 (cache, image->flags))
-            return FALSE;
-
-          /* Image data offset */
-          if (image_size > 0)
-            {
-              if (!write_card32 (cache, data_offset))
-                return FALSE;
-              data_offset += image_size;
-            }
-          else
-            {
-              if (!write_card32 (cache, 0))
-                return FALSE;
-            }
-
-          list = list->next;
-        }
-
-      /* Now write the image data */
-      list = node->image_list;
-      for (i = 0; i < len; i++, list = list->next)
-        {
-          Image *image = list->data;
-          int pixel_data_size = get_image_pixel_data_size (image);
-          int meta_data_size = get_image_meta_data_size (image);
-
-          if (get_image_data_size (image) == 0)
-            continue;
-
-          /* Pixel data */
-          if (pixel_data_size > 0)
-            {
-              image->image_data->offset = image_data_offset + 8;
-              if (!write_card32 (cache, image->image_data->offset))
-                return FALSE;
-            }
-          else
-            {
-              if (!write_card32 (cache, (guint32) (image->image_data ? image->image_data->offset : 0)))
-                return FALSE;
-            }
-
-          if (meta_data_size > 0)
-            {
-              image->icon_data->offset = image_data_offset + pixel_data_size + 8;
-              if (!write_card32 (cache, image->icon_data->offset))
-                return FALSE;
-            }
-          else
-            {
-              if (!write_card32 (cache, image->icon_data ? image->icon_data->offset : 0))
-                return FALSE;
-            }
-
-          if (pixel_data_size > 0)
-            {
-              if (!write_image_data (cache, image->image_data, image->image_data->offset))
-                return FALSE;
-            }
-
-          if (meta_data_size > 0)
-            {
-              if (!write_icon_data (cache, image->icon_data, image->icon_data->offset))
-                return FALSE;
-            }
-
-          image_data_offset += pixel_data_size + meta_data_size + 8;
-        }
-
-      *offset = next_offset;
-      node = node->next;
-    }
-
-  return TRUE;
-}
-
-static gboolean
-write_hash_table (FILE *cache, HashContext *context, int *new_offset)
-{
-  int offset = HASH_OFFSET;
-  int node_offset;
-  int i;
-
-  if (!(write_card32 (cache, context->size)))
-    return FALSE;
-
-  offset += 4;
-  node_offset = offset + context->size * 4;
-  /* Just write zeros here, we will rewrite this later */
-  for (i = 0; i < context->size; i++)
-    {
-      if (!write_card32 (cache, 0))
-       return FALSE;
-    }
-
-  /* Now write the buckets */
-  for (i = 0; i < context->size; i++)
-    {
-      if (!context->nodes[i])
-       continue;
-
-      g_assert (node_offset % 4 == 0);
-      if (!write_bucket (cache, context->nodes[i], &node_offset))
-       return FALSE;
-    }
-
-  *new_offset = node_offset;
-
-  /* Now write out the bucket offsets */
-
-  fseek (cache, offset, SEEK_SET);
-
-  for (i = 0; i < context->size; i++)
-    {
-      if (context->nodes[i] != NULL)
-        node_offset = context->nodes[i]->offset;
-      else
-       node_offset = 0xffffffff;
-      if (!write_card32 (cache, node_offset))
-        return FALSE;
-    }
-
-  fseek (cache, 0, SEEK_END);
-
-  return TRUE;
-}
-
-static gboolean
-write_dir_index (FILE *cache, int offset, GList *directories)
-{
-  int n_dirs;
-  GList *d;
-  char *dir;
-  int tmp, tmp2;
-
-  n_dirs = g_list_length (directories);
-
-  if (!write_card32 (cache, n_dirs))
-    return FALSE;
-
-  offset += 4 + n_dirs * 4;
-
-  tmp = offset;
-  for (d = directories; d; d = d->next)
-    {
-      dir = d->data;
-
-      tmp2 = find_string (dir);
-
-      if (tmp2 == 0 || tmp2 == -1)
-        {
-          tmp2 = tmp;
-          tmp += ALIGN_VALUE (strlen (dir) + 1, 4);
-          /* We're playing a little game with negative
-           * offsets here to handle duplicate strings in
-           * the array, even though that should not
-           * really happen for the directory index.
-           */
-          add_string (dir, -tmp2);
-        }
-      else if (tmp2 < 0)
-        {
-          tmp2 = -tmp2;
-        }
-
-      if (!write_card32 (cache, tmp2))
-       return FALSE;
-    }
-
-  g_assert (offset == ftell (cache));
-  for (d = directories; d; d = d->next)
-    {
-      dir = d->data;
-
-      tmp2 = find_string (dir);
-      g_assert (tmp2 != 0 && tmp2 != -1);
-      if (tmp2 < 0)
-        {
-          tmp2 = -tmp2;
-          g_assert (tmp2 == ftell (cache));
-          add_string (dir, tmp2);
-          if (!write_string (cache, dir))
-           return FALSE;
-        }
-    }
-
-  return TRUE;
-}
-
-static gboolean
-write_file (FILE *cache, GHashTable *files, GList *directories)
-{
-  HashContext context;
-  int new_offset;
-
-  /* Convert the hash table into something looking a bit more
-   * like what we want to write to disk.
-   */
-  context.size = g_spaced_primes_closest (g_hash_table_size (files) / 3);
-  context.nodes = g_new0 (HashNode *, context.size);
-
-  g_hash_table_foreach_remove (files, convert_to_hash, &context);
-
-  /* Now write the file */
-  /* We write 0 as the directory list offset and go
-   * back and change it later */
-  if (!write_header (cache, 0))
-    {
-      g_printerr (_("Failed to write header\n"));
-      return FALSE;
-    }
-
-  if (!write_hash_table (cache, &context, &new_offset))
-    {
-      g_printerr (_("Failed to write hash table\n"));
-      return FALSE;
-    }
-
-  if (!write_dir_index (cache, new_offset, directories))
-    {
-      g_printerr (_("Failed to write folder index\n"));
-      return FALSE;
-    }
-
-  rewind (cache);
-
-  if (!write_header (cache, new_offset))
-    {
-      g_printerr (_("Failed to rewrite header\n"));
-      return FALSE;
-    }
-
-  return TRUE;
-}
-
-static gboolean
-validate_file (const char *file)
-{
-  GMappedFile *map;
-  CacheInfo info;
-
-  map = g_mapped_file_new (file, FALSE, NULL);
-  if (!map)
-    return FALSE;
-
-  info.cache = g_mapped_file_get_contents (map);
-  info.cache_size = g_mapped_file_get_length (map);
-  info.n_directories = 0;
-  info.flags = CHECK_OFFSETS|CHECK_STRINGS|CHECK_PIXBUFS;
-
-  if (!gtk_icon_cache_validate (&info))
-    {
-      g_mapped_file_unref (map);
-      return FALSE;
-    }
-
-  g_mapped_file_unref (map);
-
-  return TRUE;
-}
-
-/**
- * safe_fclose:
- * @f: A FILE* stream, must have underlying fd
- *
- * Unix defaults for data preservation after system crash
- * are unspecified, and many systems will eat your data
- * in this situation unless you explicitly fsync().
- *
- * Returns: %TRUE on success, %FALSE on failure, and will set errno()
- */
-static gboolean
-safe_fclose (FILE *f)
-{
-  int fd = fileno (f);
-  g_assert (fd >= 0);
-  if (fflush (f) == EOF)
-    return FALSE;
-#ifndef G_OS_WIN32
-  if (fsync (fd) < 0)
-    return FALSE;
-#endif
-  if (fclose (f) == EOF)
-    return FALSE;
-  return TRUE;
-}
-
-static void
-build_cache (const char *path)
-{
-  char *cache_path, *tmp_cache_path;
-#ifdef G_OS_WIN32
-  char *bak_cache_path = NULL;
-#endif
-  GHashTable *files;
-  FILE *cache;
-  GStatBuf path_stat, cache_stat;
-  struct utimbuf utime_buf;
-  GList *directories = NULL;
-  int fd;
-  int retry_count = 0;
-#ifndef G_OS_WIN32
-  mode_t mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH;
-#else
-  int mode = _S_IWRITE | _S_IREAD;
-#endif
-#ifndef _O_BINARY
-#define _O_BINARY 0
-#endif
-
-  tmp_cache_path = g_build_filename (path, "."CACHE_NAME, NULL);
-  cache_path = g_build_filename (path, CACHE_NAME, NULL);
-
-opentmp:
-  if ((fd = g_open (tmp_cache_path, O_WRONLY | O_CREAT | O_EXCL | O_TRUNC | _O_BINARY, mode)) == -1)
-    {
-      if (retry_count == 0)
-        {
-          retry_count++;
-          g_remove (tmp_cache_path);
-          goto opentmp;
-        }
-      g_printerr (_("Failed to open file %s : %s\n"), tmp_cache_path, g_strerror (errno));
-      exit (1);
-    }
-
-  cache = fdopen (fd, "wb");
-
-  if (!cache)
-    {
-      g_printerr (_("Failed to write cache file: %s\n"), g_strerror (errno));
-      exit (1);
-    }
-
-  files = g_hash_table_new (g_str_hash, g_str_equal);
-  image_data_hash = g_hash_table_new (g_str_hash, g_str_equal);
-  icon_data_hash = g_hash_table_new (g_str_hash, g_str_equal);
-  string_pool = g_hash_table_new (g_str_hash, g_str_equal);
-
-  directories = scan_directory (path, NULL, files, NULL, 0);
-
-  if (g_hash_table_size (files) == 0)
-    {
-      /* Empty table, just close and remove the file */
-
-      fclose (cache);
-      g_unlink (tmp_cache_path);
-      g_unlink (cache_path);
-      exit (0);
-    }
-
-  /* FIXME: Handle failure */
-  if (!write_file (cache, files, directories))
-    {
-      g_unlink (tmp_cache_path);
-      exit (1);
-    }
-
-  if (!safe_fclose (cache))
-    {
-      g_printerr (_("Failed to write cache file: %s\n"), g_strerror (errno));
-      g_unlink (tmp_cache_path);
-      exit (1);
-    }
-  cache = NULL;
-
-  g_list_free_full (directories, g_free);
-
-  if (!validate_file (tmp_cache_path))
-    {
-      g_printerr (_("The generated cache was invalid.\n"));
-      /*g_unlink (tmp_cache_path);*/
-      exit (1);
-    }
-
-#ifdef G_OS_WIN32
-  if (g_file_test (cache_path, G_FILE_TEST_EXISTS))
-    {
-      bak_cache_path = g_strconcat (cache_path, ".bak", NULL);
-      g_unlink (bak_cache_path);
-      if (g_rename (cache_path, bak_cache_path) == -1)
-       {
-          int errsv = errno;
-
-         g_printerr (_("Could not rename %s to %s: %s, removing %s then.\n"),
-                     cache_path, bak_cache_path,
-                     g_strerror (errsv),
-                     cache_path);
-         g_unlink (cache_path);
-         bak_cache_path = NULL;
-       }
-    }
-#endif
-
-  if (g_rename (tmp_cache_path, cache_path) == -1)
-    {
-      int errsv = errno;
-
-      g_printerr (_("Could not rename %s to %s: %s\n"),
-                 tmp_cache_path, cache_path,
-                 g_strerror (errsv));
-      g_unlink (tmp_cache_path);
-#ifdef G_OS_WIN32
-      if (bak_cache_path != NULL)
-       if (g_rename (bak_cache_path, cache_path) == -1)
-          {
-            errsv = errno;
-
-            g_printerr (_("Could not rename %s back to %s: %s.\n"),
-                        bak_cache_path, cache_path,
-                        g_strerror (errsv));
-          }
-#endif
-      exit (1);
-    }
-#ifdef G_OS_WIN32
-  if (bak_cache_path != NULL)
-    g_unlink (bak_cache_path);
-#endif
-
-  /* Update time */
-  /* FIXME: What do do if an error occurs here? */
-  if (g_stat (path, &path_stat) < 0 ||
-      g_stat (cache_path, &cache_stat))
-    exit (1);
-
-  utime_buf.actime = path_stat.st_atime;
-  utime_buf.modtime = cache_stat.st_mtime;
-#if GLIB_CHECK_VERSION (2, 17, 1)
-  g_utime (path, &utime_buf);
-#else
-  utime (path, &utime_buf);
-#endif
-
-  if (!quiet)
-    g_printerr (_("Cache file created successfully.\n"));
-}
-
-static void
-write_csource (const char *path)
-{
-  char *cache_path;
-  char *data;
-  gsize len;
-  int i;
-
-  cache_path = g_build_filename (path, CACHE_NAME, NULL);
-  if (!g_file_get_contents (cache_path, &data, &len, NULL))
-    exit (1);
-
-  g_printf ("#ifdef __SUNPRO_C\n");
-  g_printf ("#pragma align 4 (%s)\n", var_name);
-  g_printf ("#endif\n");
-
-  g_printf ("#ifdef __GNUC__\n");
-  g_printf ("static const guint8 %s[] __attribute__ ((__aligned__ (4))) = \n", var_name);
-  g_printf ("#else\n");
-  g_printf ("static const guint8 %s[] = \n", var_name);
-  g_printf ("#endif\n");
-
-  g_printf ("{\n");
-  for (i = 0; i < len - 1; i++)
-    {
-      if (i %12 == 0)
-       g_printf ("  ");
-      g_printf ("0x%02x, ", (guint8)data[i]);
-      if (i % 12 == 11)
-        g_printf ("\n");
-    }
-
-  g_printf ("0x%02x\n};\n", (guint8)data[i]);
-}
-
-static GOptionEntry args[] = {
-  { "force", 'f', 0, G_OPTION_ARG_NONE, &force_update, N_("Overwrite an existing cache, even if up to date"), NULL },
-  { "ignore-theme-index", 't', 0, G_OPTION_ARG_NONE, &ignore_theme_index, N_("Don’t check for the existence of index.theme"), NULL },
-  { "index-only", 'i', 0, G_OPTION_ARG_NONE, &index_only, N_("Don’t include image data in the cache"), NULL },
-  { "include-image-data", 0, G_OPTION_FLAG_REVERSE, G_OPTION_ARG_NONE, &index_only, N_("Include image data in the cache"), NULL },
-  { "source", 'c', 0, G_OPTION_ARG_STRING, &var_name, N_("Output a C header file"), "NAME" },
-  { "quiet", 'q', 0, G_OPTION_ARG_NONE, &quiet, N_("Turn off verbose output"), NULL },
-  { "validate", 'v', 0, G_OPTION_ARG_NONE, &validate, N_("Validate existing icon cache"), NULL },
-  { NULL }
-};
-
-static void
-printerr_handler (const char *string)
-{
-  const char *charset;
-
-  fputs (g_get_prgname (), stderr);
-  fputs (": ", stderr);
-  if (g_get_charset (&charset))
-    fputs (string, stderr); /* charset is UTF-8 already */
-  else
-    {
-      char *result;
-
-      result = g_convert_with_fallback (string, -1, charset, "UTF-8", "?", NULL, NULL, NULL);
-
-      if (result)
-        {
-          fputs (result, stderr);
-          g_free (result);
-        }
-
-      fflush (stderr);
-    }
-}
-
-
-int
-main (int argc, char **argv)
-{
-  char *path;
-  GOptionContext *context;
-
-  if (argc < 2)
-    return 0;
-
-  g_set_printerr_handler (printerr_handler);
-
-  setlocale (LC_ALL, "");
-
-#ifdef ENABLE_NLS
-  bindtextdomain (GETTEXT_PACKAGE, GTK_LOCALEDIR);
-#ifdef HAVE_BIND_TEXTDOMAIN_CODESET
-  bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
-#endif
-#endif
-
-  context = g_option_context_new ("ICONPATH");
-  g_option_context_add_main_entries (context, args, GETTEXT_PACKAGE);
-
-  g_option_context_parse (context, &argc, &argv, NULL);
-
-  path = argv[1];
-#ifdef G_OS_WIN32
-  path = g_locale_to_utf8 (path, -1, NULL, NULL, NULL);
-#endif
-
-  if (validate)
-    {
-       char *file = g_build_filename (path, CACHE_NAME, NULL);
-
-       if (!g_file_test (file, G_FILE_TEST_IS_REGULAR))
-         {
-            if (!quiet)
-              g_printerr (_("File not found: %s\n"), file);
-            exit (1);
-         }
-       if (!validate_file (file))
-         {
-           if (!quiet)
-             g_printerr (_("Not a valid icon cache: %s\n"), file);
-           exit (1);
-         }
-       else
-         {
-           exit (0);
-         }
-    }
-
-  if (!ignore_theme_index && !has_theme_index (path))
-    {
-      if (path)
-       {
-         g_printerr (_("No theme index file.\n"));
-       }
-      else
-       {
-         g_printerr (_("No theme index file in “%s”.\n"
-                   "If you really want to create an icon cache here, use --ignore-theme-index.\n"), path);
-       }
-
-      return 1;
-    }
-
-  if (!force_update && is_cache_up_to_date (path))
-    return 0;
-
-  replace_backslashes_with_slashes (path);
-  build_cache (path);
-
-  if (strcmp (var_name, "-") != 0)
-    write_csource (path);
-
-  return 0;
-}
index d899561a33777f5ffacfd086ac788410f0879308..3c24e1683570450f9240ef6fd87da183cf99ac68 100644 (file)
@@ -689,6 +689,7 @@ subdir('gtk/css')
 subdir('gdk')
 subdir('gsk')
 subdir('gtk')
+subdir('tools')
 subdir('modules')
 if get_option('demos')
   subdir('demos')
diff --git a/tools/encodesymbolic.c b/tools/encodesymbolic.c
new file mode 100644 (file)
index 0000000..6f4a44f
--- /dev/null
@@ -0,0 +1,163 @@
+/* encodesymbolic.c
+ * Copyright (C) 2014  Alexander Larsson <alexl@redhat.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <glib.h>
+#include <gdk/gdk.h>
+#include <glib/gi18n.h>
+
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#ifdef G_OS_WIN32
+#include <io.h>
+#endif
+#include <errno.h>
+#include <stdlib.h>
+#include <locale.h>
+
+#include "gdkpixbufutilsprivate.h"
+
+static char *output_dir = NULL;
+
+static gboolean debug;
+
+static GOptionEntry args[] = {
+  { "output", 'o', 0, G_OPTION_ARG_FILENAME, &output_dir, N_("Output to this directory instead of cwd"), NULL },
+  { "debug", 0, 0, G_OPTION_ARG_NONE, &debug, N_("Generate debug output") },
+  { NULL }
+};
+
+int
+main (int argc, char **argv)
+{
+  char *path, *basename, *pngpath, *pngfile, *dot;
+  GOptionContext *context;
+  GdkPixbuf *symbolic;
+  GError *error;
+  int width, height;
+  char **sizev;
+  GFileOutputStream *out;
+  GFile *dest;
+  char *data;
+  gsize len;
+
+  setlocale (LC_ALL, "");
+
+#ifdef ENABLE_NLS
+  bindtextdomain (GETTEXT_PACKAGE, GTK_LOCALEDIR);
+#ifdef HAVE_BIND_TEXTDOMAIN_CODESET
+  bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
+#endif
+#endif
+
+  g_set_prgname ("gtk-encode-symbolic-svg");
+
+  context = g_option_context_new ("[OPTION…] PATH WIDTHxHEIGHT");
+  g_option_context_add_main_entries (context, args, GETTEXT_PACKAGE);
+
+  g_option_context_parse (context, &argc, &argv, NULL);
+
+  if (argc < 3)
+    {
+      g_printerr ("%s\n", g_option_context_get_help (context, FALSE, NULL));
+      return 1;
+    }
+
+  width = 0;
+  height = 0;
+  sizev = g_strsplit (argv[2], "x", 0);
+  if (g_strv_length (sizev) == 2)
+    {
+      width = atoi(sizev[0]);
+      height = atoi(sizev[1]);
+    }
+  g_strfreev (sizev);
+
+  if (width == 0 || height == 0)
+    {
+      g_printerr (_("Invalid size %s\n"), argv[2]);
+      return 1;
+    }
+
+  path = argv[1];
+#ifdef G_OS_WIN32
+  path = g_locale_to_utf8 (path, -1, NULL, NULL, NULL);
+#endif
+
+  error = NULL;
+  if (!g_file_get_contents (path, &data, &len, &error))
+    {
+      g_printerr (_("Can’t load file: %s\n"), error->message);
+      return 1;
+    }
+
+  basename = g_path_get_basename (path);
+
+  symbolic = gtk_make_symbolic_pixbuf_from_data (data, len, width, height, 1.0, debug ? basename : NULL, &error);
+  if (symbolic == NULL)
+    {
+      g_printerr (_("Can’t load file: %s\n"), error->message);
+      return 1;
+    }
+
+  g_free (data);
+
+  dot = strrchr (basename, '.');
+  if (dot != NULL)
+    *dot = 0;
+  pngfile = g_strconcat (basename, ".symbolic.png", NULL);
+  g_free (basename);
+
+  if (output_dir != NULL)
+    pngpath = g_build_filename (output_dir, pngfile, NULL);
+  else
+    pngpath = g_strdup (pngfile);
+
+  g_free (pngfile);
+
+  dest = g_file_new_for_path (pngpath);
+
+
+  out = g_file_replace (dest,
+                       NULL, FALSE,
+                       G_FILE_CREATE_REPLACE_DESTINATION,
+                       NULL, &error);
+  if (out == NULL)
+    {
+      g_printerr (_("Can’t save file %s: %s\n"), pngpath, error->message);
+      return 1;
+    }
+
+  if (!gdk_pixbuf_save_to_stream (symbolic, G_OUTPUT_STREAM (out), "png", NULL, &error, NULL))
+    {
+      g_printerr (_("Can’t save file %s: %s\n"), pngpath, error->message);
+      return 1;
+    }
+
+  if (!g_output_stream_close (G_OUTPUT_STREAM (out), NULL, &error))
+    {
+      g_printerr (_("Can’t close stream"));
+      return 1;
+    }
+
+  g_object_unref (out);
+  g_free (pngpath);
+
+  return 0;
+}
diff --git a/tools/gtk-builder-tool-enumerate.c b/tools/gtk-builder-tool-enumerate.c
new file mode 100644 (file)
index 0000000..feb3758
--- /dev/null
@@ -0,0 +1,75 @@
+/*  Copyright 2015 Red Hat, Inc.
+ *
+ * GTK+ is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * GLib is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GTK+; see the file COPYING.  If not,
+ * see <http://www.gnu.org/licenses/>.
+ *
+ * Author: Matthias Clasen
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+
+#include <glib/gi18n.h>
+#include <glib/gprintf.h>
+#include <glib/gstdio.h>
+#include <gtk/gtk.h>
+#include "gtkbuilderprivate.h"
+#include "gtk-builder-tool.h"
+
+static const char *
+object_get_id (GObject *object)
+{
+  if (GTK_IS_BUILDABLE (object))
+    return gtk_buildable_get_buildable_id (GTK_BUILDABLE (object));
+  else
+    return g_object_get_data (object, "gtk-builder-id");
+}
+
+void
+do_enumerate (int *argc, const char ***argv)
+{
+  GtkBuilder *builder;
+  GError *error = NULL;
+  int ret;
+  GSList *list, *l;
+  GObject *object;
+  const char *name;
+  const char *filename;
+
+  filename = (*argv)[1];
+
+  builder = gtk_builder_new ();
+  ret = gtk_builder_add_from_file (builder, filename, &error);
+
+  if (ret == 0)
+    {
+      g_printerr ("%s\n", error->message);
+      exit (1);
+    }
+
+  list = gtk_builder_get_objects (builder);
+  for (l = list; l; l = l->next)
+    {
+      object = l->data;
+      name = object_get_id (object);
+      if (g_str_has_prefix (name, "___") && g_str_has_suffix (name, "___"))
+        continue;
+
+      g_printf ("%s (%s)\n", name, g_type_name_from_instance ((GTypeInstance*)object));
+    }
+  g_slist_free (list);
+
+  g_object_unref (builder);
+}
diff --git a/tools/gtk-builder-tool-preview.c b/tools/gtk-builder-tool-preview.c
new file mode 100644 (file)
index 0000000..db345c7
--- /dev/null
@@ -0,0 +1,213 @@
+/*  Copyright 2015 Red Hat, Inc.
+ *
+ * GTK+ is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * GLib is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GTK+; see the file COPYING.  If not,
+ * see <http://www.gnu.org/licenses/>.
+ *
+ * Author: Matthias Clasen
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+
+#include <glib/gi18n.h>
+#include <glib/gprintf.h>
+#include <glib/gstdio.h>
+#include <gtk/gtk.h>
+#include "gtkbuilderprivate.h"
+#include "gtk-builder-tool.h"
+
+
+static void
+set_window_title (GtkWindow  *window,
+                  const char *filename,
+                  const char *id)
+{
+  char *name;
+  char *title;
+
+  name = g_path_get_basename (filename);
+
+  if (id)
+    title = g_strdup_printf ("%s in %s", id, name);
+  else
+    title = g_strdup (name);
+
+  gtk_window_set_title (window, title);
+
+  g_free (title);
+  g_free (name);
+}
+
+static void
+quit_cb (GtkWidget *widget,
+         gpointer   user_data)
+{
+  gboolean *is_done = user_data;
+
+  *is_done = TRUE;
+
+  g_main_context_wakeup (NULL);
+}
+
+static void
+preview_file (const char *filename,
+              const char *id,
+              const char *cssfile)
+{
+  GtkBuilder *builder;
+  GError *error = NULL;
+  GObject *object;
+  GtkWidget *window;
+  gboolean done = FALSE;
+
+  if (cssfile)
+    {
+      GtkCssProvider *provider;
+
+      provider = gtk_css_provider_new ();
+      gtk_css_provider_load_from_path (provider, cssfile);
+
+      gtk_style_context_add_provider_for_display (gdk_display_get_default (),
+                                                  GTK_STYLE_PROVIDER (provider),
+                                                  GTK_STYLE_PROVIDER_PRIORITY_APPLICATION);
+    }
+
+  builder = gtk_builder_new ();
+  if (!gtk_builder_add_from_file (builder, filename, &error))
+    {
+      g_printerr ("%s\n", error->message);
+      exit (1);
+    }
+
+  object = NULL;
+
+  if (id)
+    {
+      object = gtk_builder_get_object (builder, id);
+    }
+  else
+    {
+      GSList *objects, *l;
+
+      objects = gtk_builder_get_objects (builder);
+      for (l = objects; l; l = l->next)
+        {
+          GObject *obj = l->data;
+
+          if (GTK_IS_WINDOW (obj))
+            {
+              object = obj;
+              break;
+            }
+          else if (GTK_IS_WIDGET (obj))
+            {
+              if (object == NULL)
+                object = obj;
+            }
+        }
+      g_slist_free (objects);
+    }
+
+  if (object == NULL)
+    {
+      if (id)
+        g_printerr ("No object with ID '%s' found\n", id);
+      else
+        g_printerr ("No previewable object found\n");
+      exit (1);
+    }
+
+  if (!GTK_IS_WIDGET (object))
+    {
+      g_printerr ("Objects of type %s can't be previewed\n", G_OBJECT_TYPE_NAME (object));
+      exit (1);
+    }
+
+  if (GTK_IS_WINDOW (object))
+    window = GTK_WIDGET (object);
+  else
+    {
+      GtkWidget *widget = GTK_WIDGET (object);
+
+      window = gtk_window_new ();
+
+      if (GTK_IS_BUILDABLE (object))
+        id = gtk_buildable_get_buildable_id (GTK_BUILDABLE (object));
+
+      set_window_title (GTK_WINDOW (window), filename, id);
+
+      g_object_ref (widget);
+      if (gtk_widget_get_parent (widget) != NULL)
+        gtk_box_remove (GTK_BOX (gtk_widget_get_parent (widget)), widget);
+      gtk_window_set_child (GTK_WINDOW (window), widget);
+      g_object_unref (widget);
+    }
+
+  gtk_window_present (GTK_WINDOW (window));
+  g_signal_connect (window, "destroy", G_CALLBACK (quit_cb), &done);
+
+  while (!done)
+    g_main_context_iteration (NULL, TRUE);
+
+  g_object_unref (builder);
+}
+
+void
+do_preview (int          *argc,
+            const char ***argv)
+{
+  GOptionContext *context;
+  char *id = NULL;
+  char *css = NULL;
+  char **filenames = NULL;
+  const GOptionEntry entries[] = {
+    { "id", 0, 0, G_OPTION_ARG_STRING, &id, NULL, NULL },
+    { "css", 0, 0, G_OPTION_ARG_FILENAME, &css, NULL, NULL },
+    { G_OPTION_REMAINING, 0, 0, G_OPTION_ARG_FILENAME_ARRAY, &filenames, NULL, NULL },
+    { NULL, }
+  };
+  GError *error = NULL;
+
+  context = g_option_context_new (NULL);
+  g_option_context_set_help_enabled (context, FALSE);
+  g_option_context_add_main_entries (context, entries, NULL);
+
+  if (!g_option_context_parse (context, argc, (char ***)argv, &error))
+    {
+      g_printerr ("%s\n", error->message);
+      g_error_free (error);
+      exit (1);
+    }
+
+  g_option_context_free (context);
+
+  if (filenames == NULL)
+    {
+      g_printerr ("No .ui file specified\n");
+      exit (1);
+    }
+
+  if (g_strv_length (filenames) > 1)
+    {
+      g_printerr ("Can only preview a single .ui file\n");
+      exit (1);
+    }
+
+  preview_file (filenames[0], id, css);
+
+  g_strfreev (filenames);
+  g_free (id);
+  g_free (css);
+}
diff --git a/tools/gtk-builder-tool-simplify.c b/tools/gtk-builder-tool-simplify.c
new file mode 100644 (file)
index 0000000..990377b
--- /dev/null
@@ -0,0 +1,2300 @@
+/*  Copyright 2015 Red Hat, Inc.
+ *
+ * GTK+ is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * GLib is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GTK+; see the file COPYING.  If not,
+ * see <http://www.gnu.org/licenses/>.
+ *
+ * Author: Matthias Clasen
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+
+#include <glib/gi18n.h>
+#include <glib/gprintf.h>
+#include <glib/gstdio.h>
+#include <gtk/gtk.h>
+#include "gtkbuilderprivate.h"
+#include "gtk-builder-tool.h"
+
+typedef struct Element Element;
+struct Element {
+  Element *parent;
+  char *element_name;
+  char **attribute_names;
+  char **attribute_values;
+  char *data;
+  GList *children;
+
+  int line_number;
+  int char_number;
+};
+
+static void
+free_element (gpointer data)
+{
+  Element *element = data;
+  g_list_free_full (element->children, free_element);
+  g_free (element->element_name);
+  g_strfreev (element->attribute_names);
+  g_strfreev (element->attribute_values);
+  g_free (element->data);
+  g_free (element);
+}
+
+typedef struct {
+  Element *root;
+  Element *current;
+  GString *value;
+  GtkBuilder *builder;
+  const char *input_filename;
+  char *output_filename;
+  FILE *output;
+  gboolean convert3to4;
+  gboolean has_gtk_requires;
+} MyParserData;
+
+static void
+start_element (GMarkupParseContext  *context,
+               const char           *element_name,
+               const char          **attribute_names,
+               const char          **attribute_values,
+               gpointer              user_data,
+               GError              **error)
+{
+  MyParserData *data = user_data;
+  Element *elt;
+
+  elt = g_new0 (Element, 1);
+  elt->parent = data->current;
+  elt->element_name = g_strdup (element_name);
+  elt->attribute_names = g_strdupv ((char **)attribute_names);
+  elt->attribute_values = g_strdupv ((char **)attribute_values);
+
+  g_markup_parse_context_get_position (context, &elt->line_number, &elt->char_number);
+
+  if (data->current)
+    data->current->children = g_list_append (data->current->children, elt);
+  data->current = elt;
+
+  if (data->root == NULL)
+    data->root = elt;
+
+  g_string_truncate (data->value, 0);
+}
+
+static void
+end_element (GMarkupParseContext  *context,
+             const char           *element_name,
+             gpointer              user_data,
+             GError              **error)
+{
+  MyParserData *data = user_data;
+
+  data->current->data = g_strdup (data->value->str);
+
+  data->current = data->current->parent;
+}
+
+static void
+text (GMarkupParseContext  *context,
+      const char           *text,
+      gsize                 text_len,
+      gpointer              user_data,
+      GError              **error)
+{
+  MyParserData *data = user_data;
+
+  if (data->value)
+    {
+      g_string_append_len (data->value, text, text_len);
+      return;
+    }
+}
+
+static GMarkupParser parser = {
+  start_element,
+  end_element,
+  text,
+  NULL,
+  NULL
+};
+
+static const char *
+canonical_boolean_value (MyParserData *data,
+                         const char   *string)
+{
+  GValue value = G_VALUE_INIT;
+  gboolean b = FALSE;
+
+  if (gtk_builder_value_from_string_type (data->builder, G_TYPE_BOOLEAN, string, &value, NULL))
+    b = g_value_get_boolean (&value);
+
+  return b ? "1" : "0";
+}
+
+typedef enum {
+  PROP_KIND_OBJECT,
+  PROP_KIND_PACKING,
+  PROP_KIND_CELL_PACKING,
+  PROP_KIND_LAYOUT
+} PropKind;
+
+static PropKind
+get_prop_kind (Element *element)
+{
+  g_assert (g_str_equal (element->element_name, "property"));
+
+  if (g_str_equal (element->parent->element_name, "packing"))
+    return PROP_KIND_PACKING;
+  else if (g_str_equal (element->parent->element_name, "layout"))
+    return PROP_KIND_LAYOUT;
+  else if (g_str_equal (element->parent->element_name, "cell-packing"))
+    return PROP_KIND_CELL_PACKING;
+  else
+    return PROP_KIND_OBJECT;
+}
+
+/* A number of properties unfortunately can't be omitted even
+ * if they are nominally set to their default value. In many
+ * cases, this is due to subclasses not overriding the default
+ * value from the superclass.
+ */
+static gboolean
+needs_explicit_setting (GParamSpec *pspec,
+                        PropKind    kind)
+{
+  struct _Prop {
+    const char *class;
+    const char *property;
+    PropKind kind;
+  } props[] = {
+    { "GtkAboutDialog", "program-name", PROP_KIND_OBJECT },
+    { "GtkCalendar", "year", PROP_KIND_OBJECT },
+    { "GtkCalendar", "month", PROP_KIND_OBJECT },
+    { "GtkCalendar", "day", PROP_KIND_OBJECT },
+    { "GtkPlacesSidebar", "show-desktop", PROP_KIND_OBJECT },
+    { "GtkRadioButton", "draw-indicator", PROP_KIND_OBJECT },
+    { "GtkWidget", "hexpand", PROP_KIND_OBJECT },
+    { "GtkWidget", "vexpand", PROP_KIND_OBJECT },
+    { "GtkGridLayoutChild", "row", PROP_KIND_LAYOUT },
+    { "GtkGridLayoutChild", "column", PROP_KIND_LAYOUT },
+  };
+  gboolean found;
+  int k;
+  const char *class_name;
+
+  class_name = g_type_name (pspec->owner_type);
+
+  found = FALSE;
+  for (k = 0; k < G_N_ELEMENTS (props); k++)
+    {
+      if (strcmp (class_name, props[k].class) == 0 &&
+          strcmp (pspec->name, props[k].property) == 0 &&
+          kind == props[k].kind)
+        {
+          found = TRUE;
+          break;
+        }
+    }
+
+  return found;
+}
+
+static gboolean
+has_attribute (Element    *elt,
+               const char *name,
+               const char *value)
+{
+  int i;
+
+  for (i = 0; elt->attribute_names[i]; i++)
+    {
+      if (strcmp (elt->attribute_names[i], name) == 0 &&
+          (value == NULL || strcmp (elt->attribute_values[i], value) == 0))
+        return TRUE;
+    }
+
+  return FALSE;
+}
+
+static gboolean
+is_cdata_property (Element *element)
+{
+  if (g_str_equal (element->element_name, "property") &&
+      has_attribute (element, "name", "bytes") &&
+      element->parent != NULL &&
+      g_str_equal (element->parent->element_name, "object") &&
+      has_attribute (element->parent, "class", "GtkBuilderListItemFactory"))
+    return TRUE;
+
+  return FALSE;
+}
+
+static gboolean
+is_pcdata_element (Element *element)
+{
+  /* elements that can contain text */
+  const char *names[] = {
+    "property",
+    "attribute",
+    "action-widget",
+    "pattern",
+    "mime-type",
+    "col",
+    "item",
+    "mark",
+    NULL,
+  };
+
+  if (g_str_equal (element->element_name, "property") &&
+      (g_strv_contains ((const char * const *)element->attribute_names, "bind-source") ||
+       g_strv_contains ((const char * const *)element->attribute_names, "bind_source")))
+    return FALSE;
+
+  if (g_strv_contains (names, element->element_name))
+    return TRUE;
+
+  return FALSE;
+}
+
+static gboolean
+is_container_element (Element *element)
+{
+  /* elements that just hold a list of things and
+   * can be omitted when they have no children
+   */
+  const char *names[] = {
+    "packing",
+    "layout",
+    "cell-packing",
+    "attributes",
+    "action-widgets",
+    "patterns",
+    "mime-types",
+    "attributes",
+    "row",
+    "items",
+    NULL
+  };
+
+  if (g_strv_contains (names, element->element_name))
+    return TRUE;
+
+  return FALSE;
+}
+
+static void
+canonicalize_key (char *key)
+{
+  char *p;
+
+  for (p = key; *p != 0; p++)
+    {
+      char c = *p;
+
+      /* We may meet something like AtkObject::accessible-name */
+      if (c == ':' && ((p > key && p[-1] == ':') || p[1] == ':'))
+        continue;
+
+      if (c != '-' &&
+          (c < '0' || c > '9') &&
+          (c < 'A' || c > 'Z') &&
+          (c < 'a' || c > 'z'))
+        *p = '-';
+    }
+}
+
+static struct {
+  const char *class;
+  const char *layout_manager;
+} layout_managers[] = {
+  { "GtkBox", "GtkBoxLayout" },
+  { "GtkGrid", "GtkGridLayout" },
+  { "GtkFixed", "GtkFixedLayout" },
+  { "GtkFileChooserButton", "GtkBinLayout" },
+  { "GtkFileChooserWidget", "GtkBinLayout" },
+  { "GtkOverlay", "GtkOverlayLayout" }
+};
+
+static GParamSpec *
+get_property_pspec (MyParserData *data,
+                    const char   *class_name,
+                    const char   *property_name,
+                    PropKind      kind)
+{
+  GType type;
+  GObjectClass *class;
+  GParamSpec *pspec;
+  char *canonical_name;
+
+  type = g_type_from_name (class_name);
+  if (type == G_TYPE_INVALID)
+    {
+      type = gtk_builder_get_type_from_name (data->builder, class_name);
+      if (type == G_TYPE_INVALID)
+        return NULL;
+    }
+
+  class = g_type_class_ref (type);
+  canonical_name = g_strdup (property_name);
+  canonicalize_key (canonical_name);
+  switch (kind)
+    {
+    case PROP_KIND_OBJECT:
+      pspec = g_object_class_find_property (class, canonical_name);
+      break;
+
+    case PROP_KIND_PACKING:
+      pspec = NULL;
+      break;
+
+    case PROP_KIND_CELL_PACKING:
+      {
+        GObjectClass *cell_class;
+
+        /* We're just assuming that the cell layout is using a GtkCellAreaBox. */
+        cell_class = g_type_class_ref (GTK_TYPE_CELL_AREA_BOX);
+        pspec = gtk_cell_area_class_find_cell_property (GTK_CELL_AREA_CLASS (cell_class), canonical_name);
+        g_type_class_unref (cell_class);
+      }
+      break;
+
+    case PROP_KIND_LAYOUT:
+      {
+        int i;
+        const char *layout_manager = NULL;
+
+        pspec = NULL;
+
+        for (i = 0; i < G_N_ELEMENTS (layout_managers); i++)
+          {
+            if (g_str_equal (layout_managers[i].class, class_name))
+              {
+                layout_manager = layout_managers[i].layout_manager;
+                break;
+              }
+          }
+
+        if (layout_manager)
+          {
+            GtkLayoutManagerClass *layout_manager_class;
+
+            layout_manager_class = GTK_LAYOUT_MANAGER_CLASS (g_type_class_ref (g_type_from_name (layout_manager)));
+            if (layout_manager_class->layout_child_type != G_TYPE_INVALID)
+              {
+                GObjectClass *layout_child_class;
+                layout_child_class = g_type_class_ref (layout_manager_class->layout_child_type);
+                pspec = g_object_class_find_property (layout_child_class, canonical_name);
+                g_type_class_unref (layout_child_class);
+              }
+            g_type_class_unref (layout_manager_class);
+          }
+      }
+      break;
+
+    default:
+      g_assert_not_reached ();
+    }
+  g_free (canonical_name);
+  g_type_class_unref (class);
+
+  return pspec;
+}
+
+static const char *get_class_name (Element *element);
+
+static gboolean
+value_is_default (Element      *element,
+                  MyParserData *data,
+                  GParamSpec   *pspec,
+                  const char   *value_string)
+{
+  GValue value = { 0, };
+  gboolean ret;
+  GError *error = NULL;
+
+  if (g_type_is_a (G_PARAM_SPEC_VALUE_TYPE (pspec), G_TYPE_OBJECT))
+    return FALSE;
+
+  if (g_type_is_a (G_PARAM_SPEC_VALUE_TYPE (pspec), G_TYPE_BOXED))
+    return FALSE;
+
+  if (!gtk_builder_value_from_string (data->builder, pspec, value_string, &value, &error))
+    {
+      g_printerr (_("%s:%d: Couldn’t parse value for property '%s': %s\n"), data->input_filename, element->line_number, pspec->name, error->message);
+      g_error_free (error);
+      ret = FALSE;
+    }
+  else
+    {
+      /* GtkWidget::visible has a 'smart' default */
+      if (pspec->owner_type == GTK_TYPE_WIDGET &&
+          g_str_equal (pspec->name, "visible"))
+        {
+          const char *class_name = get_class_name (element);
+          GType type = g_type_from_name (class_name);
+          gboolean default_value;
+
+          if (g_type_is_a (type, GTK_TYPE_ROOT) ||
+              g_type_is_a (type, GTK_TYPE_POPOVER))
+            default_value = FALSE;
+          else
+            default_value = TRUE;
+
+          ret = g_value_get_boolean (&value) == default_value;
+        }
+      else if (pspec->owner_type == GTK_TYPE_WINDOW &&
+               (g_str_equal (pspec->name, "default-width") ||
+                g_str_equal (pspec->name, "default-height")))
+        {
+          int default_size;
+
+          default_size = g_value_get_int (&value);
+          ret = default_size <= 0;
+        }
+      else
+        ret = g_param_value_defaults (pspec, &value);
+    }
+
+  g_value_reset (&value);
+
+  return ret;
+}
+
+static const char *
+get_attribute_value (Element *element,
+                     const char *name)
+{
+  int i;
+
+  for (i = 0; element->attribute_names[i]; i++)
+    {
+      if (g_str_equal (element->attribute_names[i], name))
+        return element->attribute_values[i];
+    }
+
+  return NULL;
+}
+
+static void
+set_attribute_value (Element *element,
+                     const char *name,
+                     const char *value)
+{
+  int i;
+  int len;
+
+  for (i = 0; element->attribute_names[i]; i++)
+    {
+      if (g_str_equal (element->attribute_names[i], name))
+        {
+          g_free (element->attribute_values[i]);
+          element->attribute_values[i] = g_strdup (value);
+          return;
+        }
+    }
+
+  len = g_strv_length (element->attribute_names);
+  element->attribute_names = g_realloc_n (element->attribute_names, len + 2, sizeof (char *));
+  element->attribute_values = g_realloc_n (element->attribute_values, len + 2, sizeof (char *));
+  element->attribute_names[len] = g_strdup (name);
+  element->attribute_values[len] = g_strdup (value);
+  element->attribute_names[len + 1] = NULL;
+  element->attribute_values[len + 1] = NULL;
+}
+
+static gboolean
+element_is_object_or_template (Element *element)
+{
+  return g_str_equal (element->element_name, "object") ||
+         g_str_equal (element->element_name, "template");
+}
+
+static const char *
+get_class_name (Element *element)
+{
+  Element *parent = element->parent;
+
+  if (element_is_object_or_template (element))
+    parent = element;
+
+  if (g_str_equal (parent->element_name, "packing"))
+    parent = parent->parent->parent; /* child - object */
+  else if (g_str_equal (parent->element_name, "layout"))
+    parent = parent->parent->parent->parent; /* object - child - object */
+
+
+  if (g_str_equal (parent->element_name, "object"))
+    {
+      return get_attribute_value (parent, "class");
+    }
+  else if (g_str_equal (parent->element_name, "template"))
+    {
+      if (get_attribute_value (parent, "parent"))
+        return get_attribute_value (parent, "parent");
+      else
+        return get_attribute_value (parent, "class");
+    }
+
+  return NULL;
+}
+
+static gboolean
+property_is_boolean (Element      *element,
+                     MyParserData *data)
+{
+  GParamSpec *pspec = NULL;
+  const char *class_name;
+  const char *property_name;
+  int i;
+  PropKind kind;
+
+  kind = get_prop_kind (element);
+  class_name = get_class_name (element);
+  property_name = "";
+
+  for (i = 0; element->attribute_names[i]; i++)
+    {
+      if (strcmp (element->attribute_names[i], "name") == 0)
+        property_name = (const char *)element->attribute_values[i];
+    }
+
+  if (class_name && property_name)
+    pspec = get_property_pspec (data, class_name, property_name, kind);
+  if (pspec)
+    return G_PARAM_SPEC_VALUE_TYPE (pspec) == G_TYPE_BOOLEAN;
+
+  return FALSE;
+}
+
+static void
+warn_missing_property (Element      *element,
+                       MyParserData *data,
+                       const char   *class_name,
+                       const char   *property_name,
+                       PropKind      kind)
+{
+  const char *kind_str[] = { "", "Packing ", "Cell ", "Layout " };
+
+  g_printerr (_("%s:%d: %sproperty %s::%s not found\n"),
+              data->input_filename, element->line_number, kind_str[kind], class_name, property_name);
+}
+
+static gboolean
+property_can_be_omitted (Element      *element,
+                         MyParserData *data)
+{
+  int i;
+  gboolean bound;
+  gboolean translatable;
+  const char *class_name;
+  const char *property_name;
+  const char *value_string;
+  GParamSpec *pspec;
+  PropKind kind;
+
+  kind = get_prop_kind (element);
+  class_name = get_class_name (element);
+  property_name = "";
+  value_string = element->data;
+
+  bound = FALSE;
+  translatable = FALSE;
+  for (i = 0; element->attribute_names[i]; i++)
+    {
+      if (strcmp (element->attribute_names[i], "bind-source") == 0 ||
+          strcmp (element->attribute_names[i], "bind_source") == 0)
+        bound = TRUE;
+      else if (strcmp (element->attribute_names[i], "translatable") == 0)
+        translatable = TRUE;
+      else if (strcmp (element->attribute_names[i], "name") == 0)
+        property_name = (const char *)element->attribute_values[i];
+    }
+
+  if (translatable)
+    return FALSE;
+
+  if (bound)
+    return FALSE;
+
+  pspec = get_property_pspec (data, class_name, property_name, kind);
+  if (pspec == NULL)
+    {
+      warn_missing_property (element, data, class_name, property_name, kind);
+      return FALSE;
+    }
+
+  if (needs_explicit_setting (pspec, kind))
+    return FALSE;
+
+  return value_is_default (element, data, pspec, value_string);
+}
+
+static gboolean
+property_has_been_removed (Element      *element,
+                           MyParserData *data)
+{
+  const char *class_name;
+  const char *property_name;
+  struct _Prop {
+    const char *class;
+    const char *property;
+    PropKind kind;
+  } props[] = {
+    { "GtkActionBar", "position", PROP_KIND_PACKING },
+    { "GtkButtonBox", "secondary", PROP_KIND_PACKING },
+    { "GtkButtonBox", "non-homogeneous", PROP_KIND_PACKING },
+    { "GtkBox", "position", PROP_KIND_PACKING },
+    { "GtkBox", "pack-type", PROP_KIND_PACKING },
+    { "GtkHeaderBar", "position", PROP_KIND_PACKING },
+    { "GtkPopoverMenu", "position",PROP_KIND_PACKING },
+    { "GtkCheckButton", "draw-indicator", PROP_KIND_OBJECT },
+  };
+  char *canonical_name;
+  gboolean found;
+  int i, k;
+  PropKind kind;
+
+  kind = get_prop_kind (element);
+
+  class_name = get_class_name (element);
+  property_name = "";
+
+  for (i = 0; element->attribute_names[i]; i++)
+    {
+      if (strcmp (element->attribute_names[i], "name") == 0)
+        property_name = (const char *)element->attribute_values[i];
+    }
+
+  canonical_name = g_strdup (property_name);
+  g_strdelimit (canonical_name, "_", '-');
+
+  found = FALSE;
+  for (k = 0; k < G_N_ELEMENTS (props); k++)
+    {
+      if (strcmp (class_name, props[k].class) == 0 &&
+          strcmp (canonical_name, props[k].property) == 0 &&
+          kind == props[k].kind)
+        {
+          found = TRUE;
+          break;
+        }
+    }
+
+  g_free (canonical_name);
+
+  return found;
+}
+
+static void
+maybe_rename_property (Element *element, MyParserData *data)
+{
+  const char *class_name;
+  const char *property_name;
+  struct _Prop {
+    const char *class;
+    const char *property;
+    GType type;
+    PropKind kind;
+    const char *new_name;
+    const char *alt_names[3];
+  } props[] = {
+    /* the "replacement" property is placed *after* the "added" properties */
+    { "GtkPopover", "modal", GTK_TYPE_POPOVER, PROP_KIND_OBJECT, "autohide", { NULL, NULL, NULL } },
+    { "GtkWidget", "expand", GTK_TYPE_WIDGET, PROP_KIND_OBJECT, "vexpand", { "hexpand", NULL, NULL } },
+    { "GtkWidget", "margin", GTK_TYPE_WIDGET, PROP_KIND_OBJECT, "margin-bottom", { "margin-start", "margin-end", "margin-top" } },
+    { "GtkWidget", "margin-left", GTK_TYPE_WIDGET, PROP_KIND_OBJECT, "margin-start", { NULL, NULL, NULL } },
+    { "GtkWidget", "margin-right", GTK_TYPE_WIDGET, PROP_KIND_OBJECT, "margin-end", { NULL, NULL, NULL } },
+    { "GtkHeaderBar", "show-close-button", GTK_TYPE_HEADER_BAR, PROP_KIND_OBJECT, "show-title-buttons", { NULL, NULL, NULL } },
+    { "GtkHeaderBar", "custom-title", GTK_TYPE_HEADER_BAR, PROP_KIND_OBJECT, "title-widget", { NULL, NULL, NULL } },
+    { "GtkStack", "homogeneous", GTK_TYPE_STACK, PROP_KIND_OBJECT, "hhomogeneous", { "vhomogeneous", NULL, NULL } },
+    { "GtkImage", "pixbuf", GTK_TYPE_IMAGE, PROP_KIND_OBJECT, "file", { NULL, NULL, NULL } },
+  };
+  int i, k, l;
+  PropKind kind;
+  int prop_name_index = 0;
+  GType type;
+  char *canonical_name;
+
+  kind = get_prop_kind (element);
+
+  class_name = get_class_name (element);
+  property_name = NULL;
+
+  for (i = 0; element->attribute_names[i]; i++)
+    {
+      if (strcmp (element->attribute_names[i], "name") == 0)
+        {
+          prop_name_index = i;
+          property_name = (const char *)element->attribute_values[i];
+        }
+    }
+
+  if (property_name == NULL)
+    return;
+
+  type = g_type_from_name (class_name);
+
+  canonical_name = g_strdup (property_name);
+  g_strdelimit (canonical_name, "_", '-');
+
+  for (k = 0; k < G_N_ELEMENTS (props); k++)
+    {
+      if (g_type_is_a (type, props[k].type) &&
+          strcmp (canonical_name, props[k].property) == 0 &&
+          kind == props[k].kind)
+        {
+          g_free (element->attribute_values[prop_name_index]);
+          element->attribute_values[prop_name_index] = g_strdup (props[k].new_name);
+          for (l = 0; l < 3 && props[k].alt_names[l]; l++)
+            {
+              Element *elt;
+              GList *sibling;
+
+              elt = g_new0 (Element, 1);
+              elt->parent = element->parent;
+              elt->element_name = g_strdup (element->element_name);
+              elt->attribute_names = g_strdupv ((char **) element->attribute_names);
+              elt->attribute_values = g_strdupv ((char **) element->attribute_values);
+              elt->data = g_strdup (element->data);
+
+              g_free (elt->attribute_values[prop_name_index]);
+              elt->attribute_values[prop_name_index] = g_strdup (props[k].alt_names[l]);
+
+              sibling = g_list_find (element->parent->children, element);
+              element->parent->children = g_list_insert_before (element->parent->children,
+                                                                sibling,
+                                                                elt);
+            }
+          break;
+        }
+    }
+
+  g_free (canonical_name);
+}
+
+static Element *
+rewrite_stack_child (Element *child, MyParserData *data)
+{
+  Element *object = NULL;
+  Element *packing = NULL;
+  Element *new_object;
+  Element *prop;
+  GList *l;
+
+  if (!g_str_equal (child->element_name, "child"))
+    return child;
+
+  for (l = child->children; l; l = l->next)
+    {
+      Element *elt = l->data;
+      if (g_str_equal (elt->element_name, "object"))
+        object = elt;
+      else if (g_str_equal (elt->element_name, "packing"))
+        packing = elt;
+    }
+
+  if (!packing)
+    return child;
+
+  new_object = g_new0 (Element, 1);
+  new_object->element_name = g_strdup ("object");
+  new_object->attribute_names = g_new0 (char *, 2);
+  new_object->attribute_names[0] = g_strdup ("class");
+  new_object->attribute_values = g_new0 (char *, 2);
+  new_object->attribute_values[0] = g_strdup ("GtkStackPage");
+  new_object->children = packing->children;
+  new_object->parent = child;
+  packing->children = NULL;
+
+  prop = g_new0 (Element, 1);
+  prop->element_name = g_strdup ("property");
+  prop->attribute_names = g_new0 (char *, 2);
+  prop->attribute_names[0] = g_strdup ("name");
+  prop->attribute_values = g_new0 (char *, 2);
+  prop->attribute_values[0] = g_strdup ("child");
+  prop->children = g_list_append (prop->children, object);
+  prop->parent = new_object;
+  new_object->children = g_list_append (new_object->children, prop);
+
+  g_list_free (child->children);
+  child->children = g_list_append (NULL, new_object);
+
+  return child;
+}
+
+static void
+rewrite_stack (Element      *element,
+               MyParserData *data)
+{
+  GList *l, *new_children;
+
+  new_children = NULL;
+  for (l = element->children; l; l = l->next)
+    {
+      Element *child = l->data;
+      new_children = g_list_append (new_children, rewrite_stack_child (child, data));
+    }
+
+  g_list_free (element->children);
+  element->children = new_children;
+}
+
+static Element *
+rewrite_assistant_child (Element *child, MyParserData *data)
+{
+  Element *object = NULL;
+  Element *packing = NULL;
+  Element *new_object;
+  Element *prop;
+  GList *l;
+
+  if (!g_str_equal (child->element_name, "child"))
+    return child;
+
+  for (l = child->children; l; l = l->next)
+    {
+      Element *elt = l->data;
+      if (g_str_equal (elt->element_name, "object"))
+        object = elt;
+      else if (g_str_equal (elt->element_name, "packing"))
+        packing = elt;
+    }
+
+  if (!packing)
+    return child;
+
+  new_object = g_new0 (Element, 1);
+  new_object->element_name = g_strdup ("object");
+  new_object->attribute_names = g_new0 (char *, 2);
+  new_object->attribute_names[0] = g_strdup ("class");
+  new_object->attribute_values = g_new0 (char *, 2);
+  new_object->attribute_values[0] = g_strdup ("GtkAssistantPage");
+  new_object->children = packing->children;
+  new_object->parent = child;
+  packing->children = NULL;
+
+  prop = g_new0 (Element, 1);
+  prop->element_name = g_strdup ("property");
+  prop->attribute_names = g_new0 (char *, 2);
+  prop->attribute_names[0] = g_strdup ("name");
+  prop->attribute_values = g_new0 (char *, 2);
+  prop->attribute_values[0] = g_strdup ("child");
+  prop->children = g_list_append (prop->children, object);
+  prop->parent = new_object;
+  new_object->children = g_list_append (new_object->children, prop);
+
+  g_list_free (child->children);
+  child->children = g_list_append (NULL, new_object);
+
+  return child;
+}
+
+static void
+rewrite_assistant (Element      *element,
+                   MyParserData *data)
+{
+  GList *l, *new_children;
+
+  new_children = NULL;
+  for (l = element->children; l; l = l->next)
+    {
+      Element *child = l->data;
+      new_children = g_list_append (new_children, rewrite_assistant_child (child, data));
+    }
+
+  g_list_free (element->children);
+  element->children = new_children;
+}
+
+static Element *
+rewrite_notebook_page (Element *child, Element *tab, MyParserData *data)
+{
+  Element *object = NULL;
+  Element *tab_obj = NULL;
+  Element *packing = NULL;
+  Element *new_object;
+  Element *prop;
+  GList *l;
+
+  if (!g_str_equal (child->element_name, "child"))
+    return child;
+
+  if (has_attribute (child, "type", "tab") ||
+      has_attribute (child, "type", "action-start") ||
+      has_attribute (child, "type", "action-end"))
+    return child;
+
+  for (l = child->children; l; l = l->next)
+    {
+      Element *elt = l->data;
+      if (g_str_equal (elt->element_name, "object"))
+        object = elt;
+      else if (g_str_equal (elt->element_name, "packing"))
+        packing = elt;
+    }
+
+  if (!packing && !tab)
+    return child;
+
+  if (tab)
+    {
+      for (l = tab->children; l; l = l->next)
+        {
+          Element *elt = l->data;
+          if (g_str_equal (elt->element_name, "object"))
+            tab_obj = elt;
+        }
+    }
+
+  new_object = g_new0 (Element, 1);
+  new_object->element_name = g_strdup ("object");
+  new_object->attribute_names = g_new0 (char *, 2);
+  new_object->attribute_names[0] = g_strdup ("class");
+  new_object->attribute_values = g_new0 (char *, 2);
+  new_object->attribute_values[0] = g_strdup ("GtkNotebookPage");
+  new_object->parent = child;
+  if (packing)
+    {
+      new_object->children = packing->children;
+      packing->children = NULL;
+    }
+
+  prop = g_new0 (Element, 1);
+  prop->element_name = g_strdup ("property");
+  prop->attribute_names = g_new0 (char *, 2);
+  prop->attribute_names[0] = g_strdup ("name");
+  prop->attribute_values = g_new0 (char *, 2);
+  prop->attribute_values[0] = g_strdup ("child");
+  prop->children = g_list_append (prop->children, object);
+  prop->parent = new_object;
+  new_object->children = g_list_append (new_object->children, prop);
+
+  if (tab_obj)
+    {
+      prop = g_new0 (Element, 1);
+      prop->element_name = g_strdup ("property");
+      prop->attribute_names = g_new0 (char *, 2);
+      prop->attribute_names[0] = g_strdup ("name");
+      prop->attribute_values = g_new0 (char *, 2);
+      prop->attribute_values[0] = g_strdup ("tab");
+      prop->children = g_list_append (prop->children, tab_obj);
+      prop->parent = new_object;
+      new_object->children = g_list_append (new_object->children, prop);
+    }
+
+  g_list_free (child->children);
+  child->children = g_list_append (NULL, new_object);
+
+  return child;
+}
+
+static void
+rewrite_notebook (Element      *element,
+                  MyParserData *data)
+{
+  GList *l, *new_children;
+
+  new_children = NULL;
+  for (l = element->children; l; l = l->next)
+    {
+      Element *child = l->data;
+      Element *tab = l->next ? l->next->data : NULL;
+
+      if (tab && has_attribute (tab, "type", "tab"))
+        {
+          new_children = g_list_append (new_children, rewrite_notebook_page (child, tab, data));
+          l = l->next; /* skip the tab */
+        }
+      else
+        new_children = g_list_append (new_children, rewrite_notebook_page (child, NULL, data));
+    }
+
+  g_list_free (element->children);
+  element->children = new_children;
+}
+
+static void
+rewrite_pack_type_child (Element *element,
+                         MyParserData *data)
+{
+  Element *pack_type = NULL;
+  GList *l, *ll;
+
+  if (!g_str_equal (element->element_name, "child"))
+    return;
+
+  for (l = element->children; l; l = l->next)
+    {
+      Element *elt = l->data;
+
+      if (g_str_equal (elt->element_name, "packing"))
+        {
+          for (ll = elt->children; ll; ll = ll->next)
+            {
+              Element *elt2 = ll->data;
+
+              if (g_str_equal (elt2->element_name, "property") &&
+                  has_attribute (elt2, "name", "pack-type"))
+                {
+                  pack_type = elt2;
+                  elt->children = g_list_remove (elt->children, pack_type);
+                  if (elt->children == NULL)
+                    {
+                      element->children = g_list_remove (element->children, elt);
+                      free_element (elt);
+                    }
+                  break;
+                }
+            }
+        }
+
+      if (pack_type)
+        break;
+    }
+
+  if (pack_type)
+    {
+      char **attnames = g_new0 (char *, g_strv_length (element->attribute_names) + 2);
+      char **attvalues = g_new0 (char *, g_strv_length (element->attribute_names) + 2);
+      int i;
+
+      for (i = 0; element->attribute_names[i]; i++)
+        {
+          attnames[i] = g_strdup (element->attribute_names[i]);
+          attvalues[i] = g_strdup (element->attribute_values[i]);
+        }
+
+      attnames[i] = g_strdup ("type");
+      attvalues[i] = g_strdup (pack_type->data);
+
+      g_strfreev (element->attribute_names);
+      g_strfreev (element->attribute_values);
+
+      element->attribute_names = attnames;
+      element->attribute_values = attvalues;
+
+      free_element (pack_type);
+    }
+}
+
+static void
+rewrite_pack_type (Element *element,
+                   MyParserData *data)
+{
+  GList *l;
+
+  for (l = element->children; l; l = l->next)
+    {
+      Element *elt = l->data;
+      if (g_str_equal (elt->element_name, "child"))
+        rewrite_pack_type_child (elt, data);
+    }
+}
+
+static void
+rewrite_paned_child (Element *element,
+                     MyParserData *data,
+                     Element *child,
+                     const char *suffix)
+{
+  Element *resize = NULL;
+  Element *shrink = NULL;
+  GList *l, *ll;
+
+  for (l = child->children; l; l = l->next)
+    {
+      Element *elt = l->data;
+
+      if (g_str_equal (elt->element_name, "packing"))
+        {
+          for (ll = elt->children; ll; ll = ll->next)
+            {
+              Element *elt2 = ll->data;
+
+              if (g_str_equal (elt2->element_name, "property") &&
+                  has_attribute (elt2, "name", "resize"))
+                resize = elt2;
+              else if (g_str_equal (elt2->element_name, "property") &&
+                  has_attribute (elt2, "name", "shrink"))
+                shrink = elt2;
+            }
+          if (resize)
+            elt->children = g_list_remove (elt->children, resize);
+          if (shrink)
+            elt->children = g_list_remove (elt->children, shrink);
+          if (elt->children == NULL)
+            {
+              child->children = g_list_remove (child->children, elt);
+              free_element (elt);
+            }
+        }
+
+      if (resize || shrink)
+        break;
+    }
+
+  if (resize)
+    {
+      Element *elt;
+
+      elt = g_new0 (Element, 1);
+      elt->parent = element;
+      elt->element_name = g_strdup ("property");
+      elt->attribute_names = g_new0 (char *, 2);
+      elt->attribute_names[0] = g_strdup ("name");
+      elt->attribute_values = g_new0 (char *, 2);
+      elt->attribute_values[0] = g_strconcat ("resize-", suffix, NULL);
+      elt->data = g_strdup (resize->data);
+
+      element->children = g_list_prepend (element->children, elt);
+
+      free_element (resize);
+    }
+
+  if (shrink)
+    {
+      Element *elt;
+
+      elt = g_new0 (Element, 1);
+      elt->parent = element;
+      elt->element_name = g_strdup ("property");
+      elt->attribute_names = g_new0 (char *, 2);
+      elt->attribute_names[0] = g_strdup ("name");
+      elt->attribute_values = g_new0 (char *, 2);
+      elt->attribute_values[0] = g_strconcat ("shrink-", suffix, NULL);
+      elt->data = g_strdup (shrink->data);
+
+      element->children = g_list_prepend (element->children, elt);
+
+      free_element (shrink);
+    }
+}
+
+static void
+rewrite_paned (Element *element,
+               MyParserData *data)
+{
+  Element *child1 = NULL;
+  Element *child2 = NULL;
+  GList *l;
+
+  for (l = element->children; l; l = l->next)
+    {
+      Element *elt = l->data;
+
+      if (g_str_equal (elt->element_name, "child"))
+        {
+          if (child1 == NULL)
+            child1 = elt;
+          else if (child2 == NULL)
+            child2 = elt;
+          else
+            break;
+        }
+    }
+
+  if (child1)
+    rewrite_paned_child (element, data, child1, "start-child");
+
+  if (child2)
+    rewrite_paned_child (element, data, child2, "end-child");
+}
+
+static void
+rewrite_dialog (Element *element,
+               MyParserData *data)
+{
+  Element *content_area = NULL;
+  Element *vbox = NULL;
+  Element *action_area = NULL;
+  GList *l;
+
+  for (l = element->children; l; l = l->next)
+    {
+      Element *elt = l->data;
+
+      if (g_str_equal (elt->element_name, "child") &&
+          g_strcmp0 (get_attribute_value (elt, "internal-child"), "vbox") == 0)
+        {
+          content_area = elt;
+          break;
+        }
+    }
+
+  if (!content_area || !content_area->children)
+    return;
+
+  vbox = content_area->children->data;
+
+  for (l = vbox->children; l; l = l->next)
+    {
+      Element *elt = l->data;
+
+      if (g_str_equal (elt->element_name, "child") &&
+          g_strcmp0 (get_attribute_value (elt, "internal-child"), "action_area") == 0)
+        {
+          action_area = elt;
+          break;
+        }
+    }
+
+  if (!action_area)
+    return;
+
+  set_attribute_value (content_area, "internal-child", "content_area");
+  vbox->children = g_list_remove (vbox->children, action_area);
+  action_area->parent = element;
+  element->children = g_list_append (element->children, action_area);
+
+  for (l = action_area->children; l; l = l->next)
+    {
+      Element *elt = l->data;
+
+      if (g_str_equal (elt->element_name, "packing"))
+        {
+          action_area->children = g_list_remove (action_area->children, elt);
+          free_element (elt);
+          break;
+        }
+    }
+
+}
+
+static void
+rewrite_grid_layout_prop (Element *element,
+                          const char *attr_name,
+                          const char *old_value,
+                          const char *new_value)
+{
+  if (g_str_equal (element->element_name, "property"))
+    {
+      char *canonical_name;
+
+      canonical_name = g_strdup (old_value);
+      g_strdelimit (canonical_name, "_", '-');
+
+      if (has_attribute (element, attr_name, old_value) ||
+          has_attribute (element, attr_name, canonical_name))
+        {
+          set_attribute_value (element, attr_name, new_value);
+        }
+
+      g_free (canonical_name);
+    }
+}
+
+static void
+rewrite_grid_layout (Element *element,
+                     MyParserData *data)
+{
+  struct _Prop {
+    const char *attr_name;
+    const char *old_value;
+    const char *new_value;
+  } props[] = {
+    { "name", "left_attach", "column", },
+    { "name", "top_attach", "row", },
+    { "name", "width", "column-span", },
+    { "name", "height", "row-span", },
+  };
+  GList *l, *ll;
+
+  for (l = element->children; l; l = l->next)
+    {
+      Element *child = l->data;
+
+      if (g_str_equal (child->element_name, "child"))
+        {
+          Element *object = NULL;
+          Element *packing = NULL;
+
+          for (ll = child->children; ll; ll = ll->next)
+            {
+              Element *elt2 = ll->data;
+
+              if (g_str_equal (elt2->element_name, "object"))
+                object = elt2;
+
+              if (g_str_equal (elt2->element_name, "packing"))
+                packing = elt2;
+            }
+
+          if (object && packing)
+            {
+              int i;
+
+              child->children = g_list_remove (child->children, packing);
+
+              g_free (packing->element_name);
+              packing->element_name = g_strdup ("layout");
+
+              packing->parent = object;
+              object->children = g_list_append (object->children, packing);
+
+              for (ll = packing->children; ll; ll = ll->next)
+                {
+                  Element *elt = ll->data;
+
+                  for (i = 0; i < G_N_ELEMENTS (props); i++)
+                    rewrite_grid_layout_prop (elt,
+                                              props[i].attr_name,
+                                              props[i].old_value,
+                                              props[i].new_value);
+                }
+            }
+        }
+    }
+}
+
+static Element *
+add_element (Element    *parent,
+             const char *element_name)
+{
+  Element *child;
+
+  child = g_new0 (Element, 1);
+  child->parent = parent;
+  child->element_name = g_strdup (element_name);
+  child->attribute_names = g_new0 (char *, 1);
+  child->attribute_values = g_new0 (char *, 1);
+  parent->children = g_list_prepend (parent->children, child);
+
+  return child;
+}
+
+static Element *
+write_box_prop (Element *element,
+                Element *parent,
+                const char *name,
+                const char *value)
+{
+
+  if (element)
+    g_free (element->data);
+  else
+    {
+      element = add_element (parent, "property");
+      set_attribute_value (element, "name", name);
+    }
+  element->data = g_strdup (value);
+
+  return element;
+}
+
+static void
+rewrite_box (Element *element,
+             MyParserData *data)
+{
+  GList *l, *ll;
+  GtkOrientation orientation = GTK_ORIENTATION_HORIZONTAL;
+
+  if (g_str_equal (get_class_name (element), "GtkVBox"))
+    write_box_prop (NULL, element, "orientation", "vertical");
+
+  if (!g_str_equal (get_class_name (element), "GtkBox"))
+    set_attribute_value (element, "class", "GtkBox");
+
+  for (l = element->children; l; l = l->next)
+    {
+      Element *child = l->data;
+
+      if (g_str_equal (child->element_name, "property"))
+        {
+          if (has_attribute (child, "name", "orientation"))
+            {
+              GValue value = G_VALUE_INIT;
+
+              if (gtk_builder_value_from_string_type (data->builder,
+                                                      GTK_TYPE_ORIENTATION,
+                                                      child->data,
+                                                      &value,
+                                                      NULL))
+                orientation = g_value_get_enum (&value);
+            }
+        }
+    }
+
+  for (l = element->children; l; l = l->next)
+    {
+      Element *child = l->data;
+      if (g_str_equal (child->element_name, "child"))
+        {
+          Element *object = NULL;
+          Element *packing = NULL;
+
+          for (ll = child->children; ll; ll = ll->next)
+            {
+              Element *elt2 = ll->data;
+
+              if (g_str_equal (elt2->element_name, "object"))
+                object = elt2;
+
+              if (g_str_equal (elt2->element_name, "packing"))
+                packing = elt2;
+            }
+
+          if (object && packing)
+            {
+              Element *halign = NULL;
+              Element *hexpand = NULL;
+              Element *valign = NULL;
+              Element *vexpand = NULL;
+
+              gboolean expand = FALSE;
+              gboolean fill = TRUE;
+
+              for (ll = object->children; ll; ll = ll->next)
+                {
+                  Element *elt = ll->data;
+                  if (g_str_equal (elt->element_name, "property"))
+                    {
+                      if (has_attribute (elt, "name", "halign"))
+                        halign = elt;
+                      else if (has_attribute (elt, "name", "hexpand"))
+                        hexpand = elt;
+                      else if (has_attribute (elt, "name", "valign"))
+                        valign = elt;
+                      else if (has_attribute (elt, "name", "vexpand"))
+                        vexpand = elt;
+                    }
+                }
+
+              for (ll = packing->children; ll; ll = ll->next)
+                {
+                  Element *elt = ll->data;
+
+                  if (has_attribute (elt, "name", "expand"))
+                    {
+                      GValue value = G_VALUE_INIT;
+
+                      if (gtk_builder_value_from_string_type (data->builder,
+                                                              G_TYPE_BOOLEAN,
+                                                              elt->data,
+                                                              &value,
+                                                              NULL))
+                        expand = g_value_get_boolean (&value);
+                    }
+
+                  if (has_attribute (elt, "name", "fill"))
+                    {
+                      GValue value = G_VALUE_INIT;
+
+                      if (gtk_builder_value_from_string_type (data->builder,
+                                                              G_TYPE_BOOLEAN,
+                                                              elt->data,
+                                                              &value,
+                                                              NULL))
+                        fill = g_value_get_boolean (&value);
+                    }
+                }
+
+              if (orientation == GTK_ORIENTATION_HORIZONTAL)
+                {
+                  if (expand)
+                    hexpand = write_box_prop (hexpand, object, "hexpand", "1");
+                  if (!fill)
+                    halign = write_box_prop (halign, object, "halign", "center");
+                }
+              else if (orientation == GTK_ORIENTATION_VERTICAL)
+                {
+                  if (expand)
+                    vexpand = write_box_prop (vexpand, object, "vexpand", "1");
+                  if (!fill)
+                    valign = write_box_prop (valign, object, "valign", "center");
+                }
+
+              child->children = g_list_remove (child->children, packing);
+              free_element (packing);
+            }
+        }
+    }
+}
+
+static void
+rewrite_bin_child (Element      *element,
+                   MyParserData *data)
+{
+  GList *l, *ll;
+  const char *class_name;
+  GType type;
+
+  for (l = element->children; l; l = l->next)
+    {
+      Element *child = l->data;
+      Element *object = NULL;
+
+      if (!g_str_equal (child->element_name, "child") ||
+          has_attribute (child, "type", NULL))
+        continue;
+
+      for (ll = child->children; ll; ll = ll->next)
+        {
+          Element *elem = ll->data;
+
+          if (!g_str_equal (elem->element_name, "object"))
+            continue;
+
+          class_name = get_attribute_value (elem, "class");
+          if (!class_name)
+            continue;
+
+          type = g_type_from_name (class_name);
+          if (!g_type_is_a (type, GTK_TYPE_WIDGET))
+            continue;
+
+          object = elem;
+        }
+
+      if (object)
+        {
+          g_free (child->element_name);
+          g_strfreev (child->attribute_names);
+          g_strfreev (child->attribute_values);
+          child->element_name = g_strdup ("property");
+          child->attribute_names = g_new0 (char *, 2);
+          child->attribute_names[0] = g_strdup ("name");
+          child->attribute_values = g_new0 (char *, 2);
+          child->attribute_values[0] = g_strdup ("child");
+          break;
+        }
+    }
+}
+
+static gboolean
+remove_boolean_prop (Element      *element,
+                     MyParserData *data,
+                     const char   *prop_name,
+                     gboolean     *value)
+{
+  GList *l;
+
+  for (l = element->children; l; l = l->next)
+    {
+      Element *child = l->data;
+
+      if (g_str_equal (child->element_name, "property") &&
+          has_attribute (child, "name", prop_name))
+        {
+          *value = strcmp (canonical_boolean_value (data, child->data), "1") == 0;
+          element->children = g_list_remove (element->children, child);
+          free_element (child);
+          return TRUE;
+        }
+    }
+
+  return FALSE;
+}
+
+static void
+rewrite_radio_button (Element      *element,
+                      MyParserData *data)
+{
+  gboolean draw_indicator = TRUE;
+
+  if (!remove_boolean_prop (element, data, "draw-indicator", &draw_indicator))
+    remove_boolean_prop (element, data, "draw_indicator", &draw_indicator);
+
+  if (draw_indicator)
+    set_attribute_value (element, "class", "GtkCheckButton");
+  else
+    set_attribute_value (element, "class", "GtkToggleButton");
+
+}
+
+static gboolean
+has_prop (Element      *element,
+          MyParserData *data,
+          const char   *prop_name)
+{
+  GList *l;
+
+  for (l = element->children; l; l = l->next)
+    {
+      Element *child = l->data;
+
+      if (g_str_equal (child->element_name, "property") &&
+          has_attribute (child, "name", prop_name))
+        return TRUE;
+    }
+
+  return FALSE;
+}
+
+static void
+rewrite_scale (Element      *element,
+               MyParserData *data)
+{
+  if (!has_prop (element, data, "draw-value") &&
+      !has_prop (element, data, "draw_value"))
+    {
+      Element *child;
+      child = add_element (element, "property");
+      set_attribute_value (child, "name", "draw-value");
+      child->data = g_strdup ("1");
+    }
+}
+
+static void
+rewrite_requires (Element      *element,
+                  MyParserData *data)
+{
+  if (has_attribute (element, "lib", "gtk+"))
+    {
+      set_attribute_value (element, "lib", "gtk");
+      set_attribute_value (element, "version", "4.0");
+    }
+}
+
+static void
+rewrite_overlay (Element      *element,
+                 MyParserData *data)
+{
+  GList *l, *ll;
+
+  for (l = element->children; l; l = l->next)
+    {
+      Element *child = l->data;
+
+      if (g_str_equal (child->element_name, "child"))
+        {
+          Element *object = NULL;
+          Element *packing = NULL;
+
+          for (ll = child->children; ll; ll = ll->next)
+            {
+              Element *elt2 = ll->data;
+
+              if (g_str_equal (elt2->element_name, "object"))
+                object = elt2;
+
+              if (g_str_equal (elt2->element_name, "packing"))
+                packing = elt2;
+            }
+
+          if (object && packing)
+            {
+              child->children = g_list_remove (child->children, packing);
+
+              for (ll = packing->children; ll; ll = ll->next)
+                {
+                  Element *elt2 = ll->data;
+
+                  if (g_str_equal (elt2->element_name, "property") &&
+                      (has_attribute (elt2, "name", "pass-through") ||
+                       has_attribute (elt2, "name", "pass_through")))
+                    {
+                      const char *b = canonical_boolean_value (data, elt2->data);
+                      if (g_str_equal (b, "1"))
+                        {
+                          Element *new_prop;
+
+                          new_prop = add_element (object, "property");
+                          set_attribute_value (new_prop, "name", "can-target");
+                          new_prop->data = g_strdup ("0");
+                        }
+                      break;
+                    }
+                }
+
+              free_element (packing);
+            }
+        }
+    }
+}
+
+static void
+rewrite_toolbar (Element      *element,
+                 MyParserData *data)
+{
+  GList *l, *ll;
+  Element *style = NULL;
+
+  set_attribute_value (element, "class", "GtkBox");
+
+  for (l = element->children; l; l = l->next)
+    {
+      Element *child = l->data;
+
+      if (g_str_equal (child->element_name, "property") &&
+          (has_attribute (child, "name", "toolbar_style") ||
+           has_attribute (child, "name", "toolbar-style")))
+        {
+          element->children = g_list_remove (element->children, child);
+          free_element (child);
+          break;
+        }
+    }
+
+  for (l = element->children; l; l = l->next)
+    {
+      Element *child = l->data;
+      Element *object = NULL;
+      Element *packing = NULL;
+
+      if (g_str_equal (child->element_name, "style"))
+        style = child;
+
+      if (!g_str_equal (child->element_name, "child"))
+        continue;
+
+      for (ll = child->children; ll; ll = ll->next)
+        {
+          Element *elt2 = ll->data;
+
+          if (g_str_equal (elt2->element_name, "object"))
+            object = elt2;
+
+          if (g_str_equal (elt2->element_name, "packing"))
+            packing = elt2;
+        }
+
+      if (object)
+        {
+          const char *class_name;
+
+          class_name = get_class_name (object);
+
+          if (g_str_equal (class_name, "GtkToolButton"))
+            {
+              set_attribute_value (object, "class", "GtkButton");
+            }
+          else if (g_str_equal (class_name, "GtkToggleToolButton") ||
+                   g_str_equal (class_name, "GtkRadioToolButton"))
+            {
+              set_attribute_value (object, "class", "GtkToggleButton");
+            }
+          else if (g_str_equal (class_name, "GtkSeparatorToolItem"))
+            {
+              Element *prop;
+
+              set_attribute_value (object, "class", "GtkSeparator");
+              prop = add_element (object, "property");
+              set_attribute_value (prop, "name", "orientation");
+              prop->data = g_strdup ("vertical");
+            }
+        }
+
+      if (packing)
+        child->children = g_list_remove (child->children, packing);
+    }
+
+  if (!style)
+    style = add_element (element, "style");
+
+  set_attribute_value (add_element (style, "class"), "name", "toolbar");
+}
+
+static void
+rewrite_fixed (Element      *element,
+               MyParserData *data)
+{
+  GList *l, *ll;
+
+  for (l = element->children; l; l = l->next)
+    {
+      Element *child = l->data;
+
+      if (g_str_equal (child->element_name, "child"))
+        {
+          Element *object = NULL;
+          Element *packing = NULL;
+
+          for (ll = child->children; ll; ll = ll->next)
+            {
+              Element *elt2 = ll->data;
+
+              if (g_str_equal (elt2->element_name, "object"))
+                object = elt2;
+
+              if (g_str_equal (elt2->element_name, "packing"))
+                packing = elt2;
+            }
+
+          if (object && packing)
+            {
+              int x = 0;
+              int y = 0;
+              Element *layout;
+              Element *new_prop;
+              GskTransform *transform;
+
+              for (ll = packing->children; ll; ll = ll->next)
+                {
+                  Element *elt2 = ll->data;
+                  GValue value = G_VALUE_INIT;
+
+                  if (has_attribute (elt2, "name", "x"))
+                    {
+                      if (gtk_builder_value_from_string_type (data->builder, G_TYPE_INT, elt2->data, &value, NULL))
+                        x = g_value_get_int (&value);
+                    }
+                  else if (has_attribute (elt2, "name", "y"))
+                    {
+                      if (gtk_builder_value_from_string_type (data->builder, G_TYPE_INT, elt2->data, &value, NULL))
+                        y = g_value_get_int (&value);
+                    }
+                }
+
+              child->children = g_list_remove (child->children, packing);
+              free_element (packing);
+
+              layout = add_element (object, "layout");
+              new_prop = add_element (layout, "property");
+              set_attribute_value (new_prop, "name", "transform");
+
+              transform = gsk_transform_translate (NULL, &GRAPHENE_POINT_INIT (x, y));
+              new_prop->data = gsk_transform_to_string (transform);
+              gsk_transform_unref (transform);
+            }
+        }
+    }
+}
+
+/* returns TRUE to remove the element from the parent */
+static gboolean
+simplify_element (Element      *element,
+                  MyParserData *data)
+{
+  GList *l;
+
+  if (!is_pcdata_element (element))
+    g_clear_pointer (&element->data, g_free);
+  else if (g_str_equal (element->element_name, "property") &&
+           property_is_boolean (element, data))
+    {
+      const char *b = canonical_boolean_value (data, element->data);
+      g_free (element->data);
+      element->data = g_strdup (b);
+    }
+
+  l = element->children;
+  while (l)
+    {
+      GList *next = l->next;
+      Element *child = l->data;
+      if (simplify_element (child, data))
+        {
+          element->children = g_list_remove (element->children, child);
+          free_element (child);
+        }
+      l = next;
+    }
+
+  if (is_container_element (element) && element->children == NULL)
+    return TRUE;
+
+  if (g_str_equal (element->element_name, "property") &&
+      property_can_be_omitted (element, data))
+    return TRUE;
+
+  if (g_str_equal (element->element_name, "binding"))
+    {
+      const char *property_name = get_attribute_value (element, "name");
+      const char *class_name = get_class_name (element);
+      if (!get_property_pspec (data, class_name, property_name, PROP_KIND_OBJECT))
+        warn_missing_property (element, data, class_name, property_name, PROP_KIND_OBJECT);
+    }
+
+  return FALSE;
+}
+
+static void
+simplify_tree (MyParserData *data)
+{
+  simplify_element (data->root, data);
+}
+
+static gboolean
+rewrite_element (Element      *element,
+                 MyParserData *data)
+{
+  GList *l;
+
+  l = element->children;
+  while (l)
+    {
+      GList *next = l->next;
+      Element *child = l->data;
+      if (rewrite_element (child, data))
+        {
+          element->children = g_list_remove (element->children, child);
+          free_element (child);
+        }
+      l = next;
+    }
+
+  if (element_is_object_or_template (element) &&
+      g_str_equal (get_class_name (element), "GtkStack"))
+    rewrite_stack (element, data);
+
+  if (element_is_object_or_template (element) &&
+      g_str_equal (get_class_name (element), "GtkAssistant"))
+    rewrite_assistant (element, data);
+
+  if (element_is_object_or_template (element) &&
+      g_str_equal (get_class_name (element), "GtkNotebook"))
+    rewrite_notebook (element, data);
+
+  if (element_is_object_or_template (element) &&
+      (g_str_equal (get_class_name (element), "GtkActionBar") ||
+       g_str_equal (get_class_name (element), "GtkHeaderBar")))
+    rewrite_pack_type (element, data);
+
+  if (element_is_object_or_template (element) &&
+      g_str_equal (get_class_name (element), "GtkToolbar"))
+    rewrite_toolbar (element, data);
+
+  if (element_is_object_or_template (element) &&
+      g_str_equal (get_class_name (element), "GtkPaned"))
+    rewrite_paned (element, data);
+
+  if (element_is_object_or_template (element) &&
+      g_str_equal (get_class_name (element), "GtkDialog"))
+    rewrite_dialog (element, data);
+
+  if (element_is_object_or_template (element) &&
+      g_str_equal (get_class_name (element), "GtkOverlay"))
+    rewrite_overlay (element, data);
+
+  if (element_is_object_or_template (element) &&
+      g_str_equal (get_class_name (element), "GtkGrid"))
+    rewrite_grid_layout (element, data);
+
+  if (element_is_object_or_template (element) &&
+      (g_str_equal (get_class_name (element), "GtkHBox") ||
+       g_str_equal (get_class_name (element), "GtkVBox") ||
+       g_str_equal (get_class_name (element), "GtkBox")))
+    rewrite_box (element, data);
+
+  if (element_is_object_or_template (element) &&
+      g_str_equal (get_class_name (element), "GtkFixed"))
+    rewrite_fixed (element, data);
+
+  if (element_is_object_or_template (element) &&
+      (g_str_equal (get_class_name (element), "GtkAspectFrame") ||
+       g_str_equal (get_class_name (element), "GtkComboBox") ||
+       g_str_equal (get_class_name (element), "GtkComboBoxText") ||
+       g_str_equal (get_class_name (element), "GtkFlowBoxChild") ||
+       g_str_equal (get_class_name (element), "GtkFrame") ||
+       g_str_equal (get_class_name (element), "GtkListBoxRow") ||
+       g_str_equal (get_class_name (element), "GtkOverlay") ||
+       g_str_equal (get_class_name (element), "GtkPopover") ||
+       g_str_equal (get_class_name (element), "GtkPopoverMenu") ||
+       g_str_equal (get_class_name (element), "GtkRevealer") ||
+       g_str_equal (get_class_name (element), "GtkScrolledWindow") ||
+       g_str_equal (get_class_name (element), "GtkSearchBar") ||
+       g_str_equal (get_class_name (element), "GtkViewport") ||
+       g_str_equal (get_class_name (element), "GtkWindow")))
+    rewrite_bin_child (element, data);
+
+  if (element_is_object_or_template (element) &&
+      g_str_equal (get_class_name (element), "GtkRadioButton"))
+    rewrite_radio_button (element, data);
+
+  if (element_is_object_or_template (element) &&
+      g_str_equal (get_class_name (element), "GtkScale"))
+    rewrite_scale (element, data);
+
+  if (g_str_equal (element->element_name, "property"))
+    maybe_rename_property (element, data);
+
+  if (g_str_equal (element->element_name, "property") &&
+      property_has_been_removed (element, data))
+    return TRUE;
+
+  if (g_str_equal (element->element_name, "requires"))
+    rewrite_requires (element, data);
+
+  return FALSE;
+}
+
+static void
+rewrite_tree (MyParserData *data)
+{
+  rewrite_element (data->root, data);
+}
+
+/* For properties which have changed their default
+ * value between 3 and 4, we make sure that their
+ * old default value is present in the tree before
+ * simplifying it.
+ *
+ * So far, this is just GtkWidget::visible,
+ * changing its default from 0 to 1.
+ */
+static void
+add_old_default_properties (Element      *element,
+                            MyParserData *data)
+{
+  const char *class_name;
+  GType type;
+
+  if (!g_str_equal (element->element_name, "object"))
+    return;
+
+  class_name = get_class_name (element);
+  type = g_type_from_name (class_name);
+  if (g_type_is_a (type, GTK_TYPE_WIDGET))
+    {
+      GList *l;
+      gboolean has_visible = FALSE;
+
+      for (l = element->children; l; l = l->next)
+        {
+          Element *prop = l->data;
+          const char *name = get_attribute_value (prop, "name");
+
+          if (g_str_equal (prop->element_name, "property") &&
+              g_str_equal (name, "visible"))
+            has_visible = TRUE;
+        }
+
+      if (!has_visible)
+        {
+          Element *new_prop;
+
+          new_prop = add_element (element, "property");
+          set_attribute_value (new_prop, "name", "visible");
+          new_prop->data = g_strdup ("0");
+        }
+    }
+}
+
+static void
+enhance_element (Element      *element,
+                 MyParserData *data)
+{
+  GList *l;
+
+  if (strcmp (element->element_name, "requires") == 0 &&
+      has_attribute (element, "lib", "gtk+"))
+    {
+      data->has_gtk_requires = TRUE;
+    }
+
+  add_old_default_properties (element, data);
+
+  for (l = element->children; l; l = l->next)
+    {
+      Element *child = l->data;
+      enhance_element (child, data);
+    }
+
+  if (element == data->root && !data->has_gtk_requires)
+    {
+      Element *requires = add_element (element, "requires");
+      set_attribute_value (requires, "lib", "gtk+");
+      set_attribute_value (requires, "version", "3.0");
+    }
+}
+
+static void
+enhance_tree (MyParserData *data)
+{
+  enhance_element (data->root, data);
+}
+
+static void
+dump_element (Element *element,
+              FILE    *output,
+              int      indent)
+{
+  g_fprintf (output, "%*s<%s", indent, "", element->element_name);
+  if (element->attribute_names[0])
+    {
+      int i;
+      for (i = 0; element->attribute_names[i]; i++)
+        {
+          char *escaped = g_markup_escape_text (element->attribute_values[i], -1);
+          g_fprintf (output, " %s=\"%s\"", element->attribute_names[i], escaped);
+          g_free (escaped);
+        }
+    }
+  if (element->children || element->data)
+    {
+      g_fprintf (output, ">");
+
+      if (element->children)
+        {
+          GList *l;
+
+          g_fprintf (output, "\n");
+          for (l = element->children; l; l = l->next)
+            {
+              Element *child = l->data;
+              dump_element (child, output, indent + 2);
+            }
+          g_fprintf (output, "%*s", indent, "");
+        }
+      else
+        {
+          if (is_cdata_property (element))
+            {
+              g_fprintf (output, "<![CDATA[");
+              g_fprintf (output, "%s", element->data);
+              g_fprintf (output, "]]>");
+            }
+          else
+            {
+              char *escaped = g_markup_escape_text (element->data, -1);
+              g_fprintf (output, "%s", escaped);
+              g_free (escaped);
+            }
+        }
+      g_fprintf (output, "</%s>\n", element->element_name);
+    }
+  else
+    g_fprintf (output, "/>\n");
+}
+
+static void
+write_xml_declaration (FILE *output)
+{
+  g_fprintf (output, "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n");
+}
+
+static void
+dump_tree (MyParserData *data)
+{
+  write_xml_declaration (data->output);
+  dump_element (data->root, data->output, 0);
+}
+
+static gboolean
+simplify_file (const char *filename,
+               gboolean    replace,
+               gboolean    convert3to4)
+{
+  GMarkupParseContext *context;
+  char *buffer;
+  MyParserData data;
+  GError *error = NULL;
+
+  data.input_filename = filename;
+  data.output_filename = NULL;
+  data.convert3to4 = convert3to4;
+  data.has_gtk_requires = FALSE;
+
+  if (replace)
+    {
+      int fd;
+      fd = g_file_open_tmp ("gtk-builder-tool-XXXXXX", &data.output_filename, NULL);
+      data.output = fdopen (fd, "w");
+    }
+  else
+    {
+      data.output = stdout;
+    }
+
+  if (!g_file_get_contents (filename, &buffer, NULL, &error))
+    {
+      g_printerr (_("Can’t load “%s”: %s\n"), filename, error->message);
+      return FALSE;
+    }
+
+  data.root = NULL;
+  data.current = NULL;
+  data.value = g_string_new ("");
+
+  context = g_markup_parse_context_new (&parser, G_MARKUP_TREAT_CDATA_AS_TEXT, &data, NULL);
+  if (!g_markup_parse_context_parse (context, buffer, -1, &error))
+    {
+      g_printerr (_("Can’t parse “%s”: %s\n"), filename, error->message);
+      return FALSE;
+    }
+
+  data.builder = gtk_builder_new ();
+
+  if (data.convert3to4)
+    {
+      enhance_tree (&data);
+      rewrite_tree (&data);
+    }
+  simplify_tree (&data);
+
+  dump_tree (&data);
+
+  fclose (data.output);
+
+  if (data.output_filename)
+    {
+      char *content;
+      gsize length;
+
+      if (!g_file_get_contents (data.output_filename, &content, &length, &error))
+        {
+          g_printerr (_("Failed to read “%s”: %s\n"), data.output_filename, error->message);
+          return FALSE;
+        }
+
+      if (!g_file_set_contents (data.input_filename, content, length, &error))
+        {
+          g_printerr (_("Failed to write %s: “%s”\n"), data.input_filename, error->message);
+          return FALSE;
+        }
+    }
+
+  return TRUE;
+}
+
+void
+do_simplify (int          *argc,
+             const char ***argv)
+{
+  gboolean replace = FALSE;
+  gboolean convert3to4 = FALSE;
+  char **filenames = NULL;
+  GOptionContext *ctx;
+  const GOptionEntry entries[] = {
+    { "replace", 0, 0, G_OPTION_ARG_NONE, &replace, NULL, NULL },
+    { "3to4", 0, 0, G_OPTION_ARG_NONE, &convert3to4, NULL, NULL },
+    { G_OPTION_REMAINING, 0, 0, G_OPTION_ARG_FILENAME_ARRAY, &filenames, NULL, NULL },
+    { NULL, }
+  };
+  GError *error = NULL;
+  int i;
+
+  ctx = g_option_context_new (NULL);
+  g_option_context_set_help_enabled (ctx, FALSE);
+  g_option_context_add_main_entries (ctx, entries, NULL);
+
+  if (!g_option_context_parse (ctx, argc, (char ***)argv, &error))
+    {
+      g_printerr ("%s\n", error->message);
+      g_error_free (error);
+      exit (1);
+    }
+
+  g_option_context_free (ctx);
+
+  if (filenames == NULL)
+    {
+      g_printerr (_("No .ui file specified\n"));
+      exit (1);
+    }
+
+  if (g_strv_length (filenames) > 1 && !replace)
+    {
+      g_printerr (_("Can only simplify a single .ui file without --replace\n"));
+      exit (1);
+    }
+
+  for (i = 0; filenames[i]; i++)
+    {
+      if (!simplify_file (filenames[i], replace, convert3to4))
+        exit (1);
+    }
+}
diff --git a/tools/gtk-builder-tool-validate.c b/tools/gtk-builder-tool-validate.c
new file mode 100644 (file)
index 0000000..8551231
--- /dev/null
@@ -0,0 +1,160 @@
+/*  Copyright 2015 Red Hat, Inc.
+ *
+ * GTK+ is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * GLib is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GTK+; see the file COPYING.  If not,
+ * see <http://www.gnu.org/licenses/>.
+ *
+ * Author: Matthias Clasen
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+
+#include <glib/gi18n.h>
+#include <glib/gprintf.h>
+#include <glib/gstdio.h>
+#include <gtk/gtk.h>
+#include "gtkbuilderprivate.h"
+#include "gtk-builder-tool.h"
+
+static GType
+make_fake_type (const char *type_name,
+                const char *parent_name)
+{
+  GType parent_type;
+  GTypeQuery query;
+
+  parent_type = g_type_from_name (parent_name);
+  if (parent_type == G_TYPE_INVALID)
+    {
+      g_printerr ("Failed to lookup template parent type %s\n", parent_name);
+      exit (1);
+    }
+
+  g_type_query (parent_type, &query);
+  return g_type_register_static_simple (parent_type,
+                                        type_name,
+                                        query.class_size,
+                                        NULL,
+                                        query.instance_size,
+                                        NULL,
+                                        0);
+}
+
+static void
+do_validate_template (const char *filename,
+                      const char *type_name,
+                      const char *parent_name)
+{
+  GType template_type;
+  GObject *object;
+  GtkBuilder *builder;
+  GError *error = NULL;
+  int ret;
+
+  /* Only make a fake type if it doesn't exist yet.
+   * This lets us e.g. validate the GtkFileChooserWidget template.
+   */
+  template_type = g_type_from_name (type_name);
+  if (template_type == G_TYPE_INVALID)
+    template_type = make_fake_type (type_name, parent_name);
+
+  object = g_object_new (template_type, NULL);
+  if (!object)
+    {
+      g_printerr ("Failed to create an instance of the template type %s\n", type_name);
+      exit (1);
+    }
+
+  builder = gtk_builder_new ();
+  ret = gtk_builder_extend_with_template (builder, object , template_type, " ", 1, &error);
+  if (ret)
+    ret = gtk_builder_add_from_file (builder, filename, &error);
+  g_object_unref (builder);
+
+  if (ret == 0)
+    {
+      g_printerr ("%s\n", error->message);
+      exit (1);
+    }
+}
+
+static gboolean
+parse_template_error (const char   *message,
+                      char        **class_name,
+                      char        **parent_name)
+{
+  char *p;
+
+  p = strstr (message, "(class '");
+  if (p)
+    {
+      *class_name = g_strdup (p + strlen ("(class '"));
+      p = strstr (*class_name, "'");
+      if (p)
+        *p = '\0';
+    }
+  p = strstr (message, ", parent '");
+  if (p)
+    {
+      *parent_name = g_strdup (p + strlen (", parent '"));
+      p = strstr (*parent_name, "'");
+      if (p)
+        *p = '\0';
+    }
+
+  return TRUE;
+}
+
+static gboolean
+validate_file (const char *filename)
+{
+  GtkBuilder *builder;
+  GError *error = NULL;
+  int ret;
+  char *class_name = NULL;
+  char *parent_name = NULL;
+
+  builder = gtk_builder_new ();
+  ret = gtk_builder_add_from_file (builder, filename, &error);
+  g_object_unref (builder);
+
+  if (ret == 0)
+    {
+      if (g_error_matches (error, GTK_BUILDER_ERROR, GTK_BUILDER_ERROR_UNHANDLED_TAG)  &&
+          parse_template_error (error->message, &class_name, &parent_name))
+        {
+          do_validate_template (filename, class_name, parent_name);
+        }
+      else
+        {
+          g_printerr ("%s\n", error->message);
+          return FALSE;
+        }
+    }
+
+  return TRUE;
+}
+
+void
+do_validate (int *argc, const char ***argv)
+{
+  int i;
+
+  for (i = 1; i < *argc; i++)
+    {
+      if (!validate_file ((*argv)[i]))
+        exit (1);
+    }
+}
diff --git a/tools/gtk-builder-tool.c b/tools/gtk-builder-tool.c
new file mode 100644 (file)
index 0000000..bc89207
--- /dev/null
@@ -0,0 +1,141 @@
+/*  Copyright 2015 Red Hat, Inc.
+ *
+ * GTK+ is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * GLib is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GTK+; see the file COPYING.  If not,
+ * see <http://www.gnu.org/licenses/>.
+ *
+ * Author: Matthias Clasen
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+
+#include <glib/gi18n.h>
+#include <glib/gprintf.h>
+#include <glib/gstdio.h>
+#include <gtk/gtk.h>
+#include "gtkbuilderprivate.h"
+#include "gtk-builder-tool.h"
+
+static void G_GNUC_NORETURN
+usage (void)
+{
+  g_print (_("Usage:\n"
+             "  gtk-builder-tool [COMMAND] [OPTION…] FILE\n"
+             "\n"
+             "Commands:\n"
+             "  validate     Validate the file\n"
+             "  simplify     Simplify the file\n"
+             "  enumerate    List all named objects\n"
+             "  preview      Preview the file\n"
+             "\n"
+             "Simplify Options:\n"
+             "  --replace    Replace the file\n"
+             "  --3to4       Convert from GTK 3 to GTK 4\n"
+             "\n"
+             "Preview Options:\n"
+             "  --id=ID      Preview only the named object\n"
+             "  --css=FILE   Use style from CSS file\n"
+             "\n"
+             "Perform various tasks on GtkBuilder .ui files.\n"));
+  exit (1);
+}
+
+/* A simplified version of g_log_writer_default_would_drop(), to avoid
+ * bumping up the required version of GLib to 2.68
+ */
+static gboolean
+would_drop (GLogLevelFlags  level,
+            const char     *domain)
+{
+  return (level & (G_LOG_LEVEL_ERROR |
+                   G_LOG_LEVEL_CRITICAL |
+                   G_LOG_LEVEL_WARNING)) == 0;
+}
+
+static GLogWriterOutput
+log_writer_func (GLogLevelFlags   level,
+                 const GLogField *fields,
+                 gsize            n_fields,
+                 gpointer         user_data)
+{
+  gsize i;
+  const char *domain = NULL;
+  const char *message = NULL;
+
+  for (i = 0; i < n_fields; i++)
+    {
+      if (g_strcmp0 (fields[i].key, "GLIB_DOMAIN") == 0)
+        domain = fields[i].value;
+      else if (g_strcmp0 (fields[i].key, "MESSAGE") == 0)
+        message = fields[i].value;
+    }
+
+  if (message != NULL && !would_drop (level, domain))
+    {
+      const char *prefix;
+      switch (level & G_LOG_LEVEL_MASK)
+        {
+        case G_LOG_LEVEL_ERROR:
+          prefix = "ERROR";
+          break;
+        case G_LOG_LEVEL_CRITICAL:
+          prefix = "CRITICAL";
+          break;
+        case G_LOG_LEVEL_WARNING:
+          prefix = "WARNING";
+          break;
+        default:
+          prefix = "INFO";
+          break;
+        }
+      g_printerr ("%s-%s: %s\n", domain, prefix, message);
+    }
+
+  return G_LOG_WRITER_HANDLED;
+}
+
+int
+main (int argc, const char *argv[])
+{
+  g_set_prgname ("gtk-builder-tool");
+
+  g_log_set_writer_func (log_writer_func, NULL, NULL);
+
+  gtk_init ();
+
+  gtk_test_register_all_types ();
+
+  if (argc < 3)
+    usage ();
+
+  if (strcmp (argv[2], "--help") == 0)
+    usage ();
+
+  argv++;
+  argc--;
+
+  if (strcmp (argv[0], "validate") == 0)
+    do_validate (&argc, &argv);
+  else if (strcmp (argv[0], "simplify") == 0)
+    do_simplify (&argc, &argv);
+  else if (strcmp (argv[0], "enumerate") == 0)
+    do_enumerate (&argc, &argv);
+  else if (strcmp (argv[0], "preview") == 0)
+    do_preview (&argc, &argv);
+  else
+    usage ();
+
+  return 0;
+}
diff --git a/tools/gtk-builder-tool.h b/tools/gtk-builder-tool.h
new file mode 100644 (file)
index 0000000..3d895d8
--- /dev/null
@@ -0,0 +1,10 @@
+
+#ifndef __GTK_BUILDER_TOOL_H__
+#define __GTK_BUILDER_TOOL_H__
+
+void do_simplify  (int *argc, const char ***argv);
+void do_validate  (int *argc, const char ***argv);
+void do_enumerate (int *argc, const char ***argv);
+void do_preview   (int *argc, const char ***argv);
+
+#endif
diff --git a/tools/gtk-launch.c b/tools/gtk-launch.c
new file mode 100644 (file)
index 0000000..9305095
--- /dev/null
@@ -0,0 +1,200 @@
+/* GTK - The GIMP Toolkit
+ *
+ * Copyright (C) 2012 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Author: Tomas Bzatek <tbzatek@redhat.com>
+ */
+
+#include <config.h>
+
+#include <stdio.h>
+#include <unistd.h>
+#include <locale.h>
+#include <errno.h>
+
+#include <glib.h>
+#include <glib/gi18n.h>
+#include <gio/gio.h>
+#if defined(HAVE_GIO_UNIX) && !defined(__APPLE__)
+#include <gio/gdesktopappinfo.h>
+#endif
+#include <gtk.h>
+
+static gboolean show_version;
+static char **args = NULL;
+
+static GOptionEntry entries[] = {
+  { "version", 0, 0, G_OPTION_ARG_NONE, &show_version, N_("Show program version"), NULL },
+  { G_OPTION_REMAINING, 0, 0, G_OPTION_ARG_FILENAME_ARRAY, &args, NULL, NULL },
+  { NULL}
+};
+
+int
+main (int argc, char *argv[])
+{
+  GError *error = NULL;
+  GOptionContext *context = NULL;
+  char *summary;
+  char *app_name;
+#ifdef G_OS_UNIX
+  char *desktop_file_name;
+  char *bus_name = NULL;
+#endif
+  GAppInfo *info = NULL;
+  GAppLaunchContext *launch_context;
+  GList *l;
+  GFile *f;
+
+  setlocale (LC_ALL, "");
+
+#ifdef ENABLE_NLS
+  bindtextdomain (GETTEXT_PACKAGE, GTK_LOCALEDIR);
+  textdomain (GETTEXT_PACKAGE);
+#ifdef HAVE_BIND_TEXTDOMAIN_CODESET
+  bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
+#endif
+#endif
+
+  context =
+  /* Translators: this message will appear immediately after the */
+  /* usage string - Usage: COMMAND [OPTION…] <THIS_MESSAGE>    */
+    g_option_context_new (_("APPLICATION [URI…] — launch an APPLICATION"));
+
+  /* Translators: this message will appear after the usage string */
+  /* and before the list of options.                              */
+  summary = _("Launch an application (specified by its desktop file name),\n"
+              "optionally passing one or more URIs as arguments.");
+  g_option_context_set_summary (context, summary);
+  g_option_context_add_main_entries (context, entries, GETTEXT_PACKAGE);
+  g_option_context_parse (context, &argc, &argv, &error);
+
+  g_option_context_free (context);
+
+  if (error != NULL)
+    {
+      g_printerr (_("Error parsing commandline options: %s\n"), error->message);
+      g_printerr ("\n");
+      g_printerr (_("Try “%s --help” for more information."), g_get_prgname ());
+      g_printerr ("\n");
+      g_error_free (error);
+      return 1;
+    }
+
+  if (show_version)
+    {
+      g_print ("%d.%d.%d\n",
+               gtk_get_major_version (),
+               gtk_get_minor_version (),
+               gtk_get_micro_version ());
+      return 0;
+    }
+
+  if (!args)
+    {
+      /* Translators: the %s is the program name. This error message */
+      /* means the user is calling gtk-launch without any argument.  */
+      g_printerr (_("%s: missing application name"), g_get_prgname ());
+      g_printerr ("\n");
+      g_printerr (_("Try “%s --help” for more information."), g_get_prgname ());
+      g_printerr ("\n");
+      return 1;
+    }
+
+
+  gtk_init ();
+
+  app_name = *args;
+#if defined(HAVE_GIO_UNIX) && !defined(__APPLE__)
+  bus_name = g_strdup (app_name);
+  if (g_str_has_suffix (app_name, ".desktop"))
+    {
+      desktop_file_name = g_strdup (app_name);
+      bus_name[strlen (bus_name) - strlen(".desktop")] = '\0';
+    }
+  else
+    {
+      desktop_file_name = g_strconcat (app_name, ".desktop", NULL);
+    }
+
+  if (!g_dbus_is_name (bus_name))
+    g_clear_pointer (&bus_name, g_free);
+  info = G_APP_INFO (g_desktop_app_info_new (desktop_file_name));
+  g_free (desktop_file_name);
+#else
+#warning Please add support for creating AppInfo from id for your OS
+  g_printerr (_("Creating AppInfo from id not supported on non unix operating systems"));
+#endif
+  args++;
+
+  if (!info)
+    {
+      /* Translators: the first %s is the program name, the second one */
+      /* is the application name.                                      */
+      g_printerr (_("%s: no such application %s"),
+                  g_get_prgname (), app_name);
+      g_printerr ("\n");
+      return 2;
+    }
+
+  l = NULL;
+  for (; *args; args++)
+    {
+      f = g_file_new_for_commandline_arg (*args);
+      l = g_list_append (l, f);
+    }
+
+  launch_context = (GAppLaunchContext*) gdk_display_get_app_launch_context (gdk_display_get_default ());
+  if (!g_app_info_launch (info, l, launch_context, &error))
+    {
+       /* Translators: the first %s is the program name, the second one  */
+       /* is the error message.                                          */
+       g_printerr (_("%s: error launching application: %s\n"),
+                   g_get_prgname (), error->message);
+       return 3;
+    }
+  g_object_unref (info);
+  g_object_unref (launch_context);
+
+#ifdef G_OS_UNIX
+  if (bus_name != NULL)
+    {
+      GDBusConnection *connection;
+      char *object_path, *p;
+
+      connection = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, NULL);
+
+      object_path = g_strdup_printf ("/%s", bus_name);
+      for (p = object_path; *p != '\0'; p++)
+          if (*p == '.')
+              *p = '/';
+
+      if (connection)
+        g_dbus_connection_call_sync (connection,
+                                     bus_name,
+                                     object_path,
+                                     "org.freedesktop.DBus.Peer",
+                                     "Ping",
+                                     NULL, NULL,
+                                     G_DBUS_CALL_FLAGS_NONE, -1, NULL, NULL);
+      g_clear_pointer (&object_path, g_free);
+      g_clear_object (&connection);
+      g_clear_pointer (&bus_name, g_free);
+    }
+#endif
+  g_list_free_full (l, g_object_unref);
+
+  return 0;
+}
diff --git a/tools/gtk-query-settings.c b/tools/gtk-query-settings.c
new file mode 100644 (file)
index 0000000..fbaaa33
--- /dev/null
@@ -0,0 +1,95 @@
+/*  Copyright 2015 Timm Bäder
+ *
+ * GTK+ is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * GLib is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GTK+; see the file COPYING.  If not,
+ * see <http://www.gnu.org/licenses/>.
+ */
+
+#include <glib.h>
+#include <gtk/gtk.h>
+#include <string.h>
+
+
+int
+main (int argc, char **argv)
+{
+  GtkSettings  *settings;
+  GParamSpec  **props;
+  guint         n_properties;
+  guint         i;
+  int           max_prop_name_length = 0;
+  char         *pattern = NULL;
+
+  gtk_init ();
+
+  if (argc > 1)
+    pattern = argv[1];
+
+  settings = gtk_settings_get_default ();
+  props = g_object_class_list_properties (G_OBJECT_GET_CLASS (settings), &n_properties);
+
+  for (i = 0; i < n_properties; i ++)
+    {
+      int len = strlen (props[i]->name);
+
+      if (len > max_prop_name_length)
+        max_prop_name_length = len;
+    }
+
+
+  for (i = 0; i < n_properties; i ++)
+    {
+      GValue      value = {0};
+      GParamSpec *prop = props[i];
+      char       *value_str;
+      int         spacing = max_prop_name_length - strlen (prop->name) + 1;
+      gboolean    deprecated;
+
+      if (pattern && !g_strrstr (prop->name, pattern))
+        continue;
+
+      g_value_init (&value, prop->value_type);
+      g_object_get_property (G_OBJECT (settings), prop->name, &value);
+      deprecated = prop->flags & G_PARAM_DEPRECATED;
+
+      if (G_VALUE_HOLDS_ENUM (&value))
+        {
+          GEnumClass *enum_class = G_PARAM_SPEC_ENUM (prop)->enum_class;
+          GEnumValue *enum_value = g_enum_get_value (enum_class, g_value_get_enum (&value));
+
+          value_str = g_strdup (enum_value->value_name);
+        }
+      else
+        {
+          value_str = g_strdup_value_contents (&value);
+        }
+
+      if (deprecated)
+        {
+          printf ("!");
+          spacing --;
+        }
+
+      for (; spacing >= 0; spacing --)
+        printf (" ");
+
+      printf ("%s: %s\n", prop->name, value_str);
+
+      g_free (value_str);
+      g_value_unset (&value);
+    }
+
+  g_free (props);
+
+  return 0;
+}
diff --git a/tools/gtk4builder.its b/tools/gtk4builder.its
new file mode 100644 (file)
index 0000000..689ef0d
--- /dev/null
@@ -0,0 +1,23 @@
+<?xml version="1.0"?>
+<its:rules xmlns:its="http://www.w3.org/2005/11/its"
+           xmlns:gt="https://www.gnu.org/s/gettext/ns/its/extensions/1.0"
+           version="2.0">
+  <its:translateRule selector="/interface" translate="no"/>
+  <its:translateRule selector="/interface//*[@translatable = 'yes']"
+                     translate="yes"/>
+
+  <!-- The 'comment' attribute should be extracted as a translator comment.  -->
+  <its:locNoteRule selector="/interface//*[@comments]"
+                   locNotePointer="@comments"
+                   locNoteType="alert"/>
+  <gt:escapeRule selector="/interface//@comments" escape="no"/>
+
+  <!-- The 'context' attribute should be extracted as msgctxt.  -->
+  <gt:contextRule selector="/interface//*[@context]" contextPointer="@context"/>
+
+  <its:preserveSpaceRule selector="/interface" space="preserve"/>
+
+  <!-- Extracted strings are consumed by the library and are never
+       merged back; we don't want to escape special characters.  -->
+  <gt:escapeRule selector="/interface" escape="no"/>
+</its:rules>
diff --git a/tools/gtk4builder.loc b/tools/gtk4builder.loc
new file mode 100644 (file)
index 0000000..5d77e48
--- /dev/null
@@ -0,0 +1,6 @@
+<?xml version="1.0"?>
+<locatingRules>
+  <locatingRule name="GtkBuilder" pattern="*.ui">
+    <documentRule localName="interface" target="gtkbuilder.its"/>
+  </locatingRule>
+</locatingRules>
diff --git a/tools/gtk4builder.rng b/tools/gtk4builder.rng
new file mode 100644 (file)
index 0000000..1250515
--- /dev/null
@@ -0,0 +1,343 @@
+<?xml version="1.0"?>
+<grammar xmlns="http://relaxng.org/ns/structure/1.0" ns="">
+  <start>
+    <element name="interface">
+      <optional>
+        <attribute name="domain">
+          <text/>
+        </attribute>
+      </optional>
+      <zeroOrMore>
+        <choice>
+          <ref name="requires"/>
+          <ref name="object"/>
+          <ref name="template"/>
+          <ref name="menu"/>
+        </choice>
+      </zeroOrMore>
+    </element>
+  </start>
+  <define name="requires">
+    <element name="requires">
+      <attribute name="lib">
+        <text/>
+      </attribute>
+      <attribute name="version">
+        <text/>
+      </attribute>
+    </element>
+  </define>
+  <define name="object">
+    <element name="object">
+      <optional>
+        <attribute name="id">
+          <data type="ID" datatypeLibrary="http://www.w3.org/2001/XMLSchema-datatypes"/>
+        </attribute>
+      </optional>
+      <attribute name="class">
+        <text/>
+      </attribute>
+      <optional>
+        <attribute name="type-func">
+          <text/>
+        </attribute>
+      </optional>
+      <optional>
+        <attribute name="constructor">
+          <text/>
+        </attribute>
+      </optional>
+      <zeroOrMore>
+        <choice>
+          <ref name="property"/>
+          <ref name="signal"/>
+          <ref name="child"/>
+          <ref name="ANY"/>
+        </choice>
+      </zeroOrMore>
+    </element>
+  </define>
+  <define name="template">
+    <element name="template">
+      <attribute name="class">
+        <text/>
+      </attribute>
+      <attribute name="parent">
+        <text/>
+      </attribute>
+      <zeroOrMore>
+        <choice>
+          <ref name="property"/>
+          <ref name="signal"/>
+          <ref name="child"/>
+          <ref name="ANY"/>
+        </choice>
+      </zeroOrMore>
+    </element>
+  </define>
+  <define name="property">
+    <element name="property">
+      <attribute name="name">
+        <text/>
+      </attribute>
+      <optional>
+        <attribute name="translatable">
+          <choice>
+            <value>yes</value>
+            <value>no</value>
+          </choice>
+        </attribute>
+      </optional>
+      <optional>
+        <attribute name="comments">
+          <text/>
+        </attribute>
+      </optional>
+      <optional>
+        <attribute name="context">
+          <text/>
+        </attribute>
+      </optional>
+      <optional>
+        <group>
+          <attribute name="bind-source">
+            <text/>
+          </attribute>
+          <optional>
+            <attribute name="bind-property">
+              <text/>
+            </attribute>
+          </optional>
+          <optional>
+            <attribute name="bind-flags">
+              <text/>
+            </attribute>
+          </optional>
+        </group>
+      </optional>
+      <optional>
+        <text/>
+      </optional>
+    </element>
+  </define>
+  <define name="signal">
+    <element name="signal">
+      <attribute name="name">
+        <text/>
+      </attribute>
+      <attribute name="handler">
+        <text/>
+      </attribute>
+      <optional>
+        <attribute name="after">
+          <text/>
+        </attribute>
+      </optional>
+      <optional>
+        <attribute name="swapped">
+          <text/>
+        </attribute>
+      </optional>
+      <optional>
+        <attribute name="object">
+          <text/>
+        </attribute>
+      </optional>
+      <optional>
+        <attribute name="last_modification_time">
+          <text/>
+        </attribute>
+      </optional>
+      <empty/>
+    </element>
+  </define>
+  <define name="child">
+    <element name="child">
+      <optional>
+        <attribute name="type">
+          <text/>
+        </attribute>
+      </optional>
+      <optional>
+        <attribute name="internal-child">
+          <text/>
+        </attribute>
+      </optional>
+      <zeroOrMore>
+        <choice>
+          <ref name="object"/>
+          <ref name="ANY"/>
+        </choice>
+      </zeroOrMore>
+    </element>
+  </define>
+  <define name="menu">
+    <element name="menu">
+      <attribute name="id">
+        <data type="ID" datatypeLibrary="http://www.w3.org/2001/XMLSchema-datatypes"/>
+      </attribute>
+      <optional>
+        <attribute name="domain">
+          <text/>
+        </attribute>
+      </optional>
+      <zeroOrMore>
+        <choice>
+          <ref name="item"/>
+          <ref name="submenu"/>
+          <ref name="section"/>
+        </choice>
+      </zeroOrMore>
+    </element>
+  </define>
+  <define name="item">
+    <element name="item">
+      <optional>
+        <attribute name="id">
+          <data type="ID" datatypeLibrary="http://www.w3.org/2001/XMLSchema-datatypes"/>
+        </attribute>
+      </optional>
+      <zeroOrMore>
+        <choice>
+          <ref name="attribute_"/>
+          <ref name="link"/>
+        </choice>
+      </zeroOrMore>
+    </element>
+  </define>
+  <define name="attribute_">
+    <element name="attribute">
+      <attribute name="name">
+        <text/>
+      </attribute>
+      <optional>
+        <attribute name="type">
+          <text/>
+        </attribute>
+      </optional>
+      <optional>
+        <attribute name="translatable">
+          <choice>
+            <value>yes</value>
+            <value>no</value>
+          </choice>
+        </attribute>
+      </optional>
+      <optional>
+        <attribute name="context">
+          <text/>
+        </attribute>
+      </optional>
+      <optional>
+        <attribute name="comments">
+          <text/>
+        </attribute>
+      </optional>
+      <optional>
+        <text/>
+      </optional>
+    </element>
+  </define>
+  <define name="link">
+    <element name="link">
+      <optional>
+        <attribute name="id">
+          <data type="ID" datatypeLibrary="http://www.w3.org/2001/XMLSchema-datatypes"/>
+        </attribute>
+      </optional>
+      <attribute name="name">
+        <text/>
+      </attribute>
+      <zeroOrMore>
+        <ref name="item"/>
+      </zeroOrMore>
+    </element>
+  </define>
+  <define name="submenu">
+    <element name="submenu">
+      <optional>
+        <attribute name="id">
+          <data type="ID" datatypeLibrary="http://www.w3.org/2001/XMLSchema-datatypes"/>
+        </attribute>
+      </optional>
+      <zeroOrMore>
+        <choice>
+          <ref name="attribute_"/>
+          <ref name="item"/>
+          <ref name="submenu"/>
+          <ref name="section"/>
+        </choice>
+      </zeroOrMore>
+    </element>
+  </define>
+  <define name="section">
+    <element name="section">
+      <optional>
+        <attribute name="id">
+          <data type="ID" datatypeLibrary="http://www.w3.org/2001/XMLSchema-datatypes"/>
+        </attribute>
+      </optional>
+      <zeroOrMore>
+        <choice>
+          <ref name="attribute_"/>
+          <ref name="item"/>
+          <ref name="submenu"/>
+          <ref name="section"/>
+        </choice>
+      </zeroOrMore>
+    </element>
+  </define>
+  <define name="ANY">
+    <element>
+      <anyName>
+        <except>
+          <name>interface</name>
+          <name>requires</name>
+          <name>object</name>
+          <name>property</name>
+          <name>signal</name>
+          <name>child</name>
+          <name>menu</name>
+          <name>item</name>
+          <name>attribute</name>
+          <name>link</name>
+          <name>submenu</name>
+          <name>section</name>
+        </except>
+      </anyName>
+      <zeroOrMore>
+        <attribute>
+          <anyName/>
+          <text/>
+        </attribute>
+      </zeroOrMore>
+      <interleave>
+        <zeroOrMore>
+          <ref name="ALL"/>
+        </zeroOrMore>
+        <optional>
+          <text/>
+        </optional>
+      </interleave>
+    </element>
+  </define>
+  <define name="ALL">
+    <element>
+      <anyName/>
+      <zeroOrMore>
+        <attribute>
+          <anyName/>
+          <text/>
+        </attribute>
+      </zeroOrMore>
+      <interleave>
+        <zeroOrMore>
+          <ref name="ALL"/>
+        </zeroOrMore>
+        <optional>
+          <text/>
+        </optional>
+      </interleave>
+    </element>
+  </define>
+</grammar>
diff --git a/tools/meson.build b/tools/meson.build
new file mode 100644 (file)
index 0000000..e93f97d
--- /dev/null
@@ -0,0 +1,42 @@
+# Installed tools
+gtk_tools = [
+  ['gtk4-query-settings', ['gtk-query-settings.c'], []],
+  ['gtk4-builder-tool', ['gtk-builder-tool.c',
+                         'gtk-builder-tool-simplify.c',
+                         'gtk-builder-tool-validate.c',
+                         'gtk-builder-tool-enumerate.c',
+                         'gtk-builder-tool-preview.c'], [] ],
+  ['gtk4-update-icon-cache', ['updateiconcache.c'], [ libgtk_static ] ],
+  ['gtk4-encode-symbolic-svg', ['encodesymbolic.c'], [ libgtk_static ] ],
+]
+
+if os_unix
+  gtk_tools += [['gtk4-launch', ['gtk-launch.c'], []]]
+endif
+
+foreach tool: gtk_tools
+  tool_name = tool.get(0)
+  tool_srcs = tool.get(1)
+  tool_libs = tool.get(2)
+
+  exe = executable(tool_name,
+    sources: tool_srcs,
+    include_directories: [confinc],
+    c_args: common_cflags,
+    link_with: tool_libs,
+    dependencies: libgtk_dep,
+    install: true,
+  )
+
+  set_variable(tool_name.underscorify(), exe) # used in testsuites
+endforeach
+
+# Data to install
+install_data('gtk4builder.rng', install_dir: gtk_datadir / 'gtk-4.0')
+
+install_data([
+    'gtk4builder.loc',
+    'gtk4builder.its',
+  ],
+  install_dir: gtk_datadir / 'gettext/its',
+)
diff --git a/tools/updateiconcache.c b/tools/updateiconcache.c
new file mode 100644 (file)
index 0000000..4973215
--- /dev/null
@@ -0,0 +1,1771 @@
+/* updateiconcache.c
+ * Copyright (C) 2004  Anders Carlsson <andersca@gnome.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <locale.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#include <errno.h>
+#ifdef _MSC_VER
+#include <io.h>
+#include <sys/utime.h>
+#else
+#include <utime.h>
+#endif
+
+#include <glib.h>
+#include <glib/gstdio.h>
+#include <gdk-pixbuf/gdk-pixdata.h>
+#include <glib/gi18n.h>
+#include "gtkiconcachevalidatorprivate.h"
+
+static gboolean force_update = FALSE;
+static gboolean ignore_theme_index = FALSE;
+static gboolean quiet = FALSE;
+static gboolean index_only = TRUE;
+static gboolean validate = FALSE;
+static char *var_name = (char *) "-";
+
+#define CACHE_NAME "icon-theme.cache"
+
+#define HAS_SUFFIX_XPM (1 << 0)
+#define HAS_SUFFIX_SVG (1 << 1)
+#define HAS_SUFFIX_PNG (1 << 2)
+#define HAS_ICON_FILE  (1 << 3)
+
+#define MAJOR_VERSION 1
+#define MINOR_VERSION 0
+#define HASH_OFFSET 12
+
+#define ALIGN_VALUE(this, boundary) \
+  (( ((unsigned long)(this)) + (((unsigned long)(boundary)) -1)) & (~(((unsigned long)(boundary))-1)))
+
+#ifdef HAVE_FTW_H
+
+#include <ftw.h>
+
+static GStatBuf cache_dir_stat;
+static gboolean cache_up_to_date;
+
+static int check_dir_mtime (const char        *dir,
+                            const struct stat *sb,
+                            int                tf)
+{
+  if (tf != FTW_NS && sb->st_mtime > cache_dir_stat.st_mtime)
+    {
+      cache_up_to_date = FALSE;
+      /* stop tree walk */
+      return 1;
+    }
+
+  return 0;
+}
+
+static gboolean
+is_cache_up_to_date (const char *path)
+{
+  char *cache_path;
+  int retval;
+
+  cache_path = g_build_filename (path, CACHE_NAME, NULL);
+  retval = g_stat (cache_path, &cache_dir_stat);
+  g_free (cache_path);
+
+  if (retval < 0)
+    {
+      /* Cache file not found */
+      return FALSE;
+    }
+
+  cache_up_to_date = TRUE;
+
+  ftw (path, check_dir_mtime, 20);
+
+  return cache_up_to_date;
+}
+
+#else  /* !HAVE_FTW_H */
+
+gboolean
+is_cache_up_to_date (const char *path)
+{
+  GStatBuf path_stat, cache_stat;
+  char *cache_path;
+  int retval;
+
+  retval = g_stat (path, &path_stat);
+
+  if (retval < 0)
+    {
+      /* We can't stat the path,
+       * assume we have a updated cache */
+      return TRUE;
+    }
+
+  cache_path = g_build_filename (path, CACHE_NAME, NULL);
+  retval = g_stat (cache_path, &cache_stat);
+  g_free (cache_path);
+
+  if (retval < 0)
+    {
+      /* Cache file not found */
+      return FALSE;
+    }
+
+  /* Check mtime */
+  return cache_stat.st_mtime >= path_stat.st_mtime;
+}
+
+#endif  /* !HAVE_FTW_H */
+
+static gboolean
+has_theme_index (const char *path)
+{
+  gboolean result;
+  char *index_path;
+
+  index_path = g_build_filename (path, "index.theme", NULL);
+
+  result = g_file_test (index_path, G_FILE_TEST_IS_REGULAR);
+
+  g_free (index_path);
+
+  return result;
+}
+
+
+typedef struct
+{
+  GdkPixdata pixdata;
+  gboolean has_pixdata;
+  guint32 offset;
+  guint size;
+} ImageData;
+
+typedef struct
+{
+  int has_embedded_rect;
+  int x0, y0, x1, y1;
+
+  int n_attach_points;
+  int *attach_points;
+
+  int n_display_names;
+  char **display_names;
+
+  guint32 offset;
+  int size;
+} IconData;
+
+static GHashTable *image_data_hash = NULL;
+static GHashTable *icon_data_hash = NULL;
+
+typedef struct
+{
+  int flags;
+  int dir_index;
+
+  ImageData *image_data;
+  guint pixel_data_size;
+
+  IconData *icon_data;
+  guint icon_data_size;
+} Image;
+
+
+static gboolean
+foreach_remove_func (gpointer key, gpointer value, gpointer user_data)
+{
+  Image *image = (Image *)value;
+  GHashTable *files = user_data;
+  GList *list;
+  gboolean free_key = FALSE;
+
+  if (image->flags == HAS_ICON_FILE)
+    {
+      /* just a .icon file, throw away */
+      g_free (key);
+      g_free (image);
+
+      return TRUE;
+    }
+
+  list = g_hash_table_lookup (files, key);
+  if (list)
+    free_key = TRUE;
+
+  list = g_list_prepend (list, value);
+  g_hash_table_insert (files, key, list);
+
+  if (free_key)
+    g_free (key);
+
+  return TRUE;
+}
+
+static IconData *
+load_icon_data (const char *path)
+{
+  GKeyFile *icon_file;
+  char **split;
+  gsize length;
+  char *str;
+  char *split_point;
+  int i;
+  int *ivalues;
+  GError *error = NULL;
+  char **keys;
+  gsize n_keys;
+  IconData *data;
+
+  icon_file = g_key_file_new ();
+  g_key_file_set_list_separator (icon_file, ',');
+  g_key_file_load_from_file (icon_file, path, G_KEY_FILE_KEEP_TRANSLATIONS, &error);
+  if (error)
+    {
+      g_error_free (error);
+      g_key_file_free (icon_file);
+
+      return NULL;
+    }
+
+  data = g_new0 (IconData, 1);
+
+  ivalues = g_key_file_get_integer_list (icon_file,
+                                        "Icon Data", "EmbeddedTextRectangle",
+                                        &length, NULL);
+  if (ivalues)
+    {
+      if (length == 4)
+       {
+         data->has_embedded_rect = TRUE;
+         data->x0 = ivalues[0];
+         data->y0 = ivalues[1];
+         data->x1 = ivalues[2];
+         data->y1 = ivalues[3];
+       }
+
+      g_free (ivalues);
+    }
+
+  str = g_key_file_get_string (icon_file, "Icon Data", "AttachPoints", NULL);
+  if (str)
+    {
+      split = g_strsplit (str, "|", -1);
+
+      data->n_attach_points = g_strv_length (split);
+      data->attach_points = g_new (int, 2 * data->n_attach_points);
+
+      for (i = 0; i < data->n_attach_points; ++i)
+       {
+         split_point = strchr (split[i], ',');
+         if (split_point)
+           {
+             *split_point = 0;
+             split_point++;
+             data->attach_points[2 * i] = atoi (split[i]);
+             data->attach_points[2 * i + 1] = atoi (split_point);
+           }
+       }
+
+      g_strfreev (split);
+      g_free (str);
+    }
+
+  keys = g_key_file_get_keys (icon_file, "Icon Data", &n_keys, &error);
+  data->display_names = g_new0 (char *, 2 * n_keys + 1);
+  data->n_display_names = 0;
+
+  for (i = 0; i < n_keys; i++)
+    {
+      char *lang, *name;
+
+      if (g_str_has_prefix (keys[i], "DisplayName"))
+       {
+         char *open, *close = NULL;
+
+         open = strchr (keys[i], '[');
+
+         if (open)
+           close = strchr (open, ']');
+
+         if (open && close)
+           {
+             lang = g_strndup (open + 1, close - open - 1);
+             name = g_key_file_get_locale_string (icon_file,
+                                                  "Icon Data", "DisplayName",
+                                                  lang, NULL);
+           }
+         else
+           {
+             lang = g_strdup ("C");
+             name = g_key_file_get_string (icon_file,
+                                           "Icon Data", "DisplayName",
+                                           NULL);
+           }
+
+         data->display_names[2 * data->n_display_names] = lang;
+         data->display_names[2 * data->n_display_names + 1] = name;
+         data->n_display_names++;
+       }
+    }
+
+  g_strfreev (keys);
+
+  g_key_file_free (icon_file);
+
+  /* -1 means not computed yet, the real value depends
+   * on string pool state, and will be computed
+   * later
+   */
+  data->size = -1;
+
+  return data;
+}
+
+/*
+ * This function was copied from gtkfilesystemunix.c, it should
+ * probably go to GLib
+ */
+static void
+canonicalize_filename (char *filename)
+{
+  char *p, *q;
+  gboolean last_was_slash = FALSE;
+
+  p = filename;
+  q = filename;
+
+  while (*p)
+    {
+      if (*p == G_DIR_SEPARATOR)
+       {
+         if (!last_was_slash)
+           *q++ = G_DIR_SEPARATOR;
+
+         last_was_slash = TRUE;
+       }
+      else
+       {
+         if (last_was_slash && *p == '.')
+           {
+             if (*(p + 1) == G_DIR_SEPARATOR ||
+                 *(p + 1) == '\0')
+               {
+                 if (*(p + 1) == '\0')
+                   break;
+
+                 p += 1;
+               }
+             else if (*(p + 1) == '.' &&
+                      (*(p + 2) == G_DIR_SEPARATOR ||
+                       *(p + 2) == '\0'))
+               {
+                 if (q > filename + 1)
+                   {
+                     q--;
+                     while (q > filename + 1 &&
+                            *(q - 1) != G_DIR_SEPARATOR)
+                       q--;
+                   }
+
+                 if (*(p + 2) == '\0')
+                   break;
+
+                 p += 2;
+               }
+             else
+               {
+                 *q++ = *p;
+                 last_was_slash = FALSE;
+               }
+           }
+         else
+           {
+             *q++ = *p;
+             last_was_slash = FALSE;
+           }
+       }
+
+      p++;
+    }
+
+  if (q > filename + 1 && *(q - 1) == G_DIR_SEPARATOR)
+    q--;
+
+  *q = '\0';
+}
+
+static char *
+follow_links (const char *path)
+{
+  char *target;
+  char *d, *s;
+  char *path2 = NULL;
+
+  path2 = g_strdup (path);
+  while (g_file_test (path2, G_FILE_TEST_IS_SYMLINK))
+    {
+      target = g_file_read_link (path2, NULL);
+
+      if (target)
+       {
+         if (g_path_is_absolute (target))
+           path2 = target;
+         else
+           {
+             d = g_path_get_dirname (path2);
+             s = g_build_filename (d, target, NULL);
+             g_free (d);
+             g_free (target);
+             g_free (path2);
+             path2 = s;
+           }
+       }
+      else
+       break;
+    }
+
+  if (strcmp (path, path2) == 0)
+    {
+      g_free (path2);
+      path2 = NULL;
+    }
+
+  return path2;
+}
+
+static void
+maybe_cache_image_data (Image       *image,
+                       const char *path)
+{
+  if (!index_only && !image->image_data &&
+      (g_str_has_suffix (path, ".png") || g_str_has_suffix (path, ".xpm")))
+    {
+      GdkPixbuf *pixbuf;
+      ImageData *idata;
+      char *path2;
+
+      idata = g_hash_table_lookup (image_data_hash, path);
+      path2 = follow_links (path);
+
+      if (path2)
+       {
+         ImageData *idata2;
+
+         canonicalize_filename (path2);
+
+         idata2 = g_hash_table_lookup (image_data_hash, path2);
+
+         if (idata && idata2 && idata != idata2)
+           g_error ("different idatas found for symlinked '%s' and '%s'\n",
+                    path, path2);
+
+         if (idata && !idata2)
+           g_hash_table_insert (image_data_hash, g_strdup (path2), idata);
+
+         if (!idata && idata2)
+           {
+             g_hash_table_insert (image_data_hash, g_strdup (path), idata2);
+             idata = idata2;
+           }
+       }
+
+      if (!idata)
+       {
+         idata = g_new0 (ImageData, 1);
+         g_hash_table_insert (image_data_hash, g_strdup (path), idata);
+         if (path2)
+           g_hash_table_insert (image_data_hash, g_strdup (path2), idata);
+       }
+
+      if (!idata->has_pixdata)
+       {
+         pixbuf = gdk_pixbuf_new_from_file (path, NULL);
+
+         if (pixbuf)
+           {
+G_GNUC_BEGIN_IGNORE_DEPRECATIONS;
+             gdk_pixdata_from_pixbuf (&idata->pixdata, pixbuf, FALSE);
+G_GNUC_END_IGNORE_DEPRECATIONS;
+             idata->size = idata->pixdata.length + 8;
+             idata->has_pixdata = TRUE;
+           }
+       }
+
+      image->image_data = idata;
+
+      g_free (path2);
+    }
+}
+
+static void
+maybe_cache_icon_data (Image       *image,
+                       const char *path)
+{
+  if (g_str_has_suffix (path, ".icon"))
+    {
+      IconData *idata = NULL;
+      char *path2 = NULL;
+
+      idata = g_hash_table_lookup (icon_data_hash, path);
+      path2 = follow_links (path);
+
+      if (path2)
+       {
+         IconData *idata2;
+
+         canonicalize_filename (path2);
+
+         idata2 = g_hash_table_lookup (icon_data_hash, path2);
+
+         if (idata && idata2 && idata != idata2)
+           g_error ("different idatas found for symlinked '%s' and '%s'\n",
+                    path, path2);
+
+         if (idata && !idata2)
+           g_hash_table_insert (icon_data_hash, g_strdup (path2), idata);
+
+         if (!idata && idata2)
+           {
+             g_hash_table_insert (icon_data_hash, g_strdup (path), idata2);
+             idata = idata2;
+           }
+       }
+
+      if (!idata)
+       {
+         idata = load_icon_data (path);
+         g_hash_table_insert (icon_data_hash, g_strdup (path), idata);
+         if (path2)
+           g_hash_table_insert (icon_data_hash, g_strdup (path2), idata);
+        }
+
+      image->icon_data = idata;
+
+      g_free (path2);
+    }
+}
+
+/*
+ * Finds all dir separators and replaces them with “/”.
+ * This makes sure that only /-separated paths are written in cache files,
+ * maintaining compatibility with theme index files that use slashes as
+ * directory separators on all platforms.
+ */
+static void
+replace_backslashes_with_slashes (char *path)
+{
+  size_t i;
+  if (path == NULL)
+    return;
+  for (i = 0; path[i]; i++)
+    if (G_IS_DIR_SEPARATOR (path[i]))
+      path[i] = '/';
+}
+
+static GList *
+scan_directory (const char *base_path,
+               const char *subdir,
+               GHashTable  *files,
+               GList       *directories,
+               int          depth)
+{
+  GHashTable *dir_hash;
+  GDir *dir;
+  GList *list = NULL, *iterator = NULL;
+  const char *name;
+  char *dir_path;
+  gboolean dir_added = FALSE;
+  guint dir_index = 0xffff;
+
+  dir_path = g_build_path ("/", base_path, subdir, NULL);
+
+  /* FIXME: Use the gerror */
+  dir = g_dir_open (dir_path, 0, NULL);
+
+  if (!dir)
+    return directories;
+
+  dir_hash = g_hash_table_new (g_str_hash, g_str_equal);
+
+  while ((name = g_dir_read_name (dir)))
+    {
+      list = g_list_prepend (list, g_strdup (name));
+    }
+  list = g_list_sort (list, (GCompareFunc) strcmp);
+  for (iterator = list; iterator; iterator = iterator->next)
+    {
+      name = iterator->data;
+
+      char *path;
+      gboolean retval;
+      int flags = 0;
+      Image *image;
+      char *basename, *dot;
+
+      path = g_build_filename (dir_path, name, NULL);
+
+      retval = g_file_test (path, G_FILE_TEST_IS_DIR);
+      if (retval)
+       {
+         char *subsubdir;
+
+         if (subdir)
+           subsubdir = g_build_path ("/", subdir, name, NULL);
+         else
+           subsubdir = g_strdup (name);
+         directories = scan_directory (base_path, subsubdir, files,
+                                       directories, depth + 1);
+         g_free (subsubdir);
+
+         continue;
+       }
+
+      /* ignore images in the toplevel directory */
+      if (subdir == NULL)
+        continue;
+
+      retval = g_file_test (path, G_FILE_TEST_IS_REGULAR);
+      if (retval)
+       {
+         if (g_str_has_suffix (name, ".png"))
+           flags |= HAS_SUFFIX_PNG;
+         else if (g_str_has_suffix (name, ".svg"))
+           flags |= HAS_SUFFIX_SVG;
+         else if (g_str_has_suffix (name, ".xpm"))
+           flags |= HAS_SUFFIX_XPM;
+         else if (g_str_has_suffix (name, ".icon"))
+           flags |= HAS_ICON_FILE;
+
+         if (flags == 0)
+           continue;
+
+         basename = g_strdup (name);
+         dot = strrchr (basename, '.');
+         *dot = '\0';
+
+         image = g_hash_table_lookup (dir_hash, basename);
+         if (!image)
+           {
+             if (!dir_added)
+               {
+                 dir_added = TRUE;
+                 if (subdir)
+                   {
+                     dir_index = g_list_length (directories);
+                     directories = g_list_append (directories, g_strdup (subdir));
+                   }
+                 else
+                   dir_index = 0xffff;
+               }
+
+             image = g_new0 (Image, 1);
+             image->dir_index = dir_index;
+             g_hash_table_insert (dir_hash, g_strdup (basename), image);
+           }
+
+         image->flags |= flags;
+
+         maybe_cache_image_data (image, path);
+          maybe_cache_icon_data (image, path);
+
+         g_free (basename);
+       }
+
+      g_free (path);
+    }
+
+  g_list_free_full (list, g_free);
+  g_dir_close (dir);
+
+  /* Move dir into the big file hash */
+  g_hash_table_foreach_remove (dir_hash, foreach_remove_func, files);
+
+  g_hash_table_destroy (dir_hash);
+
+  return directories;
+}
+
+typedef struct _HashNode HashNode;
+
+struct _HashNode
+{
+  HashNode *next;
+  char *name;
+  GList *image_list;
+  int offset;
+};
+
+static guint
+icon_name_hash (gconstpointer key)
+{
+  const signed char *p = key;
+  guint32 h = *p;
+
+  if (h)
+    for (p += 1; *p != '\0'; p++)
+      h = (h << 5) - h + *p;
+
+  return h;
+}
+
+typedef struct {
+  int size;
+  HashNode **nodes;
+} HashContext;
+
+static gboolean
+convert_to_hash (gpointer key, gpointer value, gpointer user_data)
+{
+  HashContext *context = user_data;
+  guint hash;
+  HashNode *node;
+
+  hash = icon_name_hash (key) % context->size;
+
+  node = g_new0 (HashNode, 1);
+  node->next = NULL;
+  node->name = key;
+  node->image_list = value;
+
+  if (context->nodes[hash] != NULL)
+    node->next = context->nodes[hash];
+
+  context->nodes[hash] = node;
+
+  return TRUE;
+}
+
+static GHashTable *string_pool = NULL;
+
+static int
+find_string (const char *n)
+{
+  return GPOINTER_TO_INT (g_hash_table_lookup (string_pool, n));
+}
+
+static void
+add_string (const char *n, int offset)
+{
+  g_hash_table_insert (string_pool, (gpointer) n, GINT_TO_POINTER (offset));
+}
+
+static gboolean
+write_string (FILE *cache, const char *n)
+{
+  char *s;
+  int i, l;
+
+  l = ALIGN_VALUE (strlen (n) + 1, 4);
+
+  s = g_malloc0 (l);
+  strcpy (s, n);
+
+  i = fwrite (s, l, 1, cache);
+
+  g_free (s);
+
+  return i == 1;
+
+}
+
+static gboolean
+write_card16 (FILE *cache, guint16 n)
+{
+  int i;
+
+  n = GUINT16_TO_BE (n);
+
+  i = fwrite ((char *)&n, 2, 1, cache);
+
+  return i == 1;
+}
+
+static gboolean
+write_card32 (FILE *cache, guint32 n)
+{
+  int i;
+
+  n = GUINT32_TO_BE (n);
+
+  i = fwrite ((char *)&n, 4, 1, cache);
+
+  return i == 1;
+}
+
+
+static gboolean
+write_image_data (FILE *cache, ImageData *image_data, int offset)
+{
+  guint8 *s;
+  guint len;
+  int i;
+  GdkPixdata *pixdata = &image_data->pixdata;
+
+  /* Type 0 is GdkPixdata */
+  if (!write_card32 (cache, 0))
+    return FALSE;
+
+G_GNUC_BEGIN_IGNORE_DEPRECATIONS;
+  s = gdk_pixdata_serialize (pixdata, &len);
+G_GNUC_END_IGNORE_DEPRECATIONS;
+
+  if (!write_card32 (cache, len))
+    {
+      g_free (s);
+      return FALSE;
+    }
+
+  i = fwrite (s, len, 1, cache);
+
+  g_free (s);
+
+  return i == 1;
+}
+
+static gboolean
+write_icon_data (FILE *cache, IconData *icon_data, int offset)
+{
+  int ofs = offset + 12;
+  int j;
+  int tmp, tmp2;
+
+  if (icon_data->has_embedded_rect)
+    {
+      if (!write_card32 (cache, ofs))
+        return FALSE;
+
+       ofs += 8;
+    }
+  else
+    {
+      if (!write_card32 (cache, 0))
+        return FALSE;
+    }
+
+  if (icon_data->n_attach_points > 0)
+    {
+      if (!write_card32 (cache, ofs))
+        return FALSE;
+
+      ofs += 4 + 4 * icon_data->n_attach_points;
+    }
+  else
+    {
+      if (!write_card32 (cache, 0))
+        return FALSE;
+    }
+
+  if (icon_data->n_display_names > 0)
+    {
+      if (!write_card32 (cache, ofs))
+       return FALSE;
+    }
+  else
+    {
+      if (!write_card32 (cache, 0))
+        return FALSE;
+    }
+
+  if (icon_data->has_embedded_rect)
+    {
+      if (!write_card16 (cache, icon_data->x0) ||
+          !write_card16 (cache, icon_data->y0) ||
+         !write_card16 (cache, icon_data->x1) ||
+         !write_card16 (cache, icon_data->y1))
+        return FALSE;
+    }
+
+  if (icon_data->n_attach_points > 0)
+    {
+      if (!write_card32 (cache, icon_data->n_attach_points))
+        return FALSE;
+
+      for (j = 0; j < 2 * icon_data->n_attach_points; j++)
+        {
+          if (!write_card16 (cache, icon_data->attach_points[j]))
+            return FALSE;
+        }
+    }
+
+  if (icon_data->n_display_names > 0)
+    {
+      if (!write_card32 (cache, icon_data->n_display_names))
+        return FALSE;
+
+      ofs += 4 + 8 * icon_data->n_display_names;
+
+      tmp = ofs;
+      for (j = 0; j < 2 * icon_data->n_display_names; j++)
+        {
+          tmp2 = find_string (icon_data->display_names[j]);
+          if (tmp2 == 0 || tmp2 == -1)
+            {
+              tmp2 = tmp;
+              tmp += ALIGN_VALUE (strlen (icon_data->display_names[j]) + 1, 4);
+              /* We're playing a little game with negative
+               * offsets here to handle duplicate strings in
+               * the array.
+               */
+              add_string (icon_data->display_names[j], -tmp2);
+            }
+          else if (tmp2 < 0)
+            {
+              tmp2 = -tmp2;
+            }
+
+          if (!write_card32 (cache, tmp2))
+            return FALSE;
+
+        }
+
+      g_assert (ofs == ftell (cache));
+      for (j = 0; j < 2 * icon_data->n_display_names; j++)
+        {
+          tmp2 = find_string (icon_data->display_names[j]);
+          g_assert (tmp2 != 0 && tmp2 != -1);
+          if (tmp2 < 0)
+            {
+              tmp2 = -tmp2;
+              g_assert (tmp2 == ftell (cache));
+              add_string (icon_data->display_names[j], tmp2);
+              if (!write_string (cache, icon_data->display_names[j]))
+                return FALSE;
+            }
+        }
+    }
+
+  return TRUE;
+}
+
+static gboolean
+write_header (FILE *cache, guint32 dir_list_offset)
+{
+  return (write_card16 (cache, MAJOR_VERSION) &&
+         write_card16 (cache, MINOR_VERSION) &&
+         write_card32 (cache, HASH_OFFSET) &&
+         write_card32 (cache, dir_list_offset));
+}
+
+static int
+get_image_meta_data_size (Image *image)
+{
+  int i;
+
+  /* The complication with storing the size in both
+   * IconData and Image is necessary since we attribute
+   * the size of the IconData only to the first Image
+   * using it (at which time it is written out in the
+   * cache). Later Images just refer to the written out
+   * IconData via the offset.
+   */
+  if (image->icon_data_size == 0)
+    {
+      if (image->icon_data && image->icon_data->size < 0)
+       {
+          IconData *data = image->icon_data;
+
+          data->size = 0;
+
+          if (data->has_embedded_rect ||
+              data->n_attach_points > 0 ||
+              data->n_display_names > 0)
+            data->size += 12;
+
+          if (data->has_embedded_rect)
+            data->size += 8;
+
+          if (data->n_attach_points > 0)
+            data->size += 4 + data->n_attach_points * 4;
+
+          if (data->n_display_names > 0)
+            {
+              data->size += 4 + 8 * data->n_display_names;
+
+              for (i = 0; data->display_names[i]; i++)
+                {
+                  int poolv;
+                  if ((poolv = find_string (data->display_names[i])) == 0)
+                    {
+                      data->size += ALIGN_VALUE (strlen (data->display_names[i]) + 1, 4);
+                      /* Adding the string to the pool with -1
+                       * to indicate that it hasn't been written out
+                       * to the cache yet. We still need it in the
+                       * pool in case the same string occurs twice
+                       * during a get_single_node_size() calculation.
+                       */
+                      add_string (data->display_names[i], -1);
+                    }
+                }
+           }
+
+         image->icon_data_size = data->size;
+         data->size = 0;
+       }
+    }
+
+  g_assert (image->icon_data_size % 4 == 0);
+
+  return image->icon_data_size;
+}
+
+static int
+get_image_pixel_data_size (Image *image)
+{
+  /* The complication with storing the size in both
+   * ImageData and Image is necessary since we attribute
+   * the size of the ImageData only to the first Image
+   * using it (at which time it is written out in the
+   * cache). Later Images just refer to the written out
+   * ImageData via the offset.
+   */
+  if (image->pixel_data_size == 0)
+    {
+      if (image->image_data &&
+         image->image_data->has_pixdata)
+       {
+         image->pixel_data_size = image->image_data->size;
+         image->image_data->size = 0;
+       }
+    }
+
+  g_assert (image->pixel_data_size % 4 == 0);
+
+  return image->pixel_data_size;
+}
+
+static int
+get_image_data_size (Image *image)
+{
+  int len;
+
+  len = 0;
+
+  len += get_image_pixel_data_size (image);
+  len += get_image_meta_data_size (image);
+
+  /* Even if len is zero, we need to reserve space to
+   * write the ImageData, unless this is an .svg without
+   * .icon, in which case both image_data and icon_data
+   * are NULL.
+   */
+  if (len > 0 || image->image_data || image->icon_data)
+    len += 8;
+
+  return len;
+}
+
+static void
+get_single_node_size (HashNode *node, int *node_size, int *image_data_size)
+{
+  GList *list;
+
+  /* Node pointers */
+  *node_size = 12;
+
+  /* Name */
+  if (find_string (node->name) == 0)
+    {
+      *node_size += ALIGN_VALUE (strlen (node->name) + 1, 4);
+      add_string (node->name, -1);
+    }
+
+  /* Image list */
+  *node_size += 4 + g_list_length (node->image_list) * 8;
+
+  /* Image data */
+  *image_data_size = 0;
+  for (list = node->image_list; list; list = list->next)
+    {
+      Image *image = list->data;
+
+      *image_data_size += get_image_data_size (image);
+    }
+}
+
+static gboolean
+write_bucket (FILE *cache, HashNode *node, int *offset)
+{
+  while (node != NULL)
+    {
+      int node_size, image_data_size;
+      int next_offset, image_data_offset;
+      int data_offset;
+      int name_offset;
+      int name_size;
+      int image_list_offset;
+      int i, len;
+      GList *list;
+
+      g_assert (*offset == ftell (cache));
+
+      node->offset = *offset;
+
+      get_single_node_size (node, &node_size, &image_data_size);
+      g_assert (node_size % 4 == 0);
+      g_assert (image_data_size % 4 == 0);
+      image_data_offset = *offset + node_size;
+      next_offset = *offset + node_size + image_data_size;
+      /* Chain offset */
+      if (node->next != NULL)
+        {
+          if (!write_card32 (cache, next_offset))
+            return FALSE;
+        }
+      else
+        {
+          if (!write_card32 (cache, 0xffffffff))
+            return FALSE;
+        }
+
+      name_size = 0;
+      name_offset = find_string (node->name);
+      if (name_offset <= 0)
+        {
+          name_offset = *offset + 12;
+          name_size = ALIGN_VALUE (strlen (node->name) + 1, 4);
+          add_string (node->name, name_offset);
+        }
+      if (!write_card32 (cache, name_offset))
+        return FALSE;
+
+      image_list_offset = *offset + 12 + name_size;
+      if (!write_card32 (cache, image_list_offset))
+        return FALSE;
+
+      /* Icon name */
+      if (name_size > 0)
+        {
+          if (!write_string (cache, node->name))
+            return FALSE;
+        }
+
+      /* Image list */
+      len = g_list_length (node->image_list);
+      if (!write_card32 (cache, len))
+        return FALSE;
+
+      list = node->image_list;
+      data_offset = image_data_offset;
+      for (i = 0; i < len; i++)
+        {
+          Image *image = list->data;
+          int image_size = get_image_data_size (image);
+
+          /* Directory index */
+          if (!write_card16 (cache, image->dir_index))
+            return FALSE;
+
+          /* Flags */
+          if (!write_card16 (cache, image->flags))
+            return FALSE;
+
+          /* Image data offset */
+          if (image_size > 0)
+            {
+              if (!write_card32 (cache, data_offset))
+                return FALSE;
+              data_offset += image_size;
+            }
+          else
+            {
+              if (!write_card32 (cache, 0))
+                return FALSE;
+            }
+
+          list = list->next;
+        }
+
+      /* Now write the image data */
+      list = node->image_list;
+      for (i = 0; i < len; i++, list = list->next)
+        {
+          Image *image = list->data;
+          int pixel_data_size = get_image_pixel_data_size (image);
+          int meta_data_size = get_image_meta_data_size (image);
+
+          if (get_image_data_size (image) == 0)
+            continue;
+
+          /* Pixel data */
+          if (pixel_data_size > 0)
+            {
+              image->image_data->offset = image_data_offset + 8;
+              if (!write_card32 (cache, image->image_data->offset))
+                return FALSE;
+            }
+          else
+            {
+              if (!write_card32 (cache, (guint32) (image->image_data ? image->image_data->offset : 0)))
+                return FALSE;
+            }
+
+          if (meta_data_size > 0)
+            {
+              image->icon_data->offset = image_data_offset + pixel_data_size + 8;
+              if (!write_card32 (cache, image->icon_data->offset))
+                return FALSE;
+            }
+          else
+            {
+              if (!write_card32 (cache, image->icon_data ? image->icon_data->offset : 0))
+                return FALSE;
+            }
+
+          if (pixel_data_size > 0)
+            {
+              if (!write_image_data (cache, image->image_data, image->image_data->offset))
+                return FALSE;
+            }
+
+          if (meta_data_size > 0)
+            {
+              if (!write_icon_data (cache, image->icon_data, image->icon_data->offset))
+                return FALSE;
+            }
+
+          image_data_offset += pixel_data_size + meta_data_size + 8;
+        }
+
+      *offset = next_offset;
+      node = node->next;
+    }
+
+  return TRUE;
+}
+
+static gboolean
+write_hash_table (FILE *cache, HashContext *context, int *new_offset)
+{
+  int offset = HASH_OFFSET;
+  int node_offset;
+  int i;
+
+  if (!(write_card32 (cache, context->size)))
+    return FALSE;
+
+  offset += 4;
+  node_offset = offset + context->size * 4;
+  /* Just write zeros here, we will rewrite this later */
+  for (i = 0; i < context->size; i++)
+    {
+      if (!write_card32 (cache, 0))
+       return FALSE;
+    }
+
+  /* Now write the buckets */
+  for (i = 0; i < context->size; i++)
+    {
+      if (!context->nodes[i])
+       continue;
+
+      g_assert (node_offset % 4 == 0);
+      if (!write_bucket (cache, context->nodes[i], &node_offset))
+       return FALSE;
+    }
+
+  *new_offset = node_offset;
+
+  /* Now write out the bucket offsets */
+
+  fseek (cache, offset, SEEK_SET);
+
+  for (i = 0; i < context->size; i++)
+    {
+      if (context->nodes[i] != NULL)
+        node_offset = context->nodes[i]->offset;
+      else
+       node_offset = 0xffffffff;
+      if (!write_card32 (cache, node_offset))
+        return FALSE;
+    }
+
+  fseek (cache, 0, SEEK_END);
+
+  return TRUE;
+}
+
+static gboolean
+write_dir_index (FILE *cache, int offset, GList *directories)
+{
+  int n_dirs;
+  GList *d;
+  char *dir;
+  int tmp, tmp2;
+
+  n_dirs = g_list_length (directories);
+
+  if (!write_card32 (cache, n_dirs))
+    return FALSE;
+
+  offset += 4 + n_dirs * 4;
+
+  tmp = offset;
+  for (d = directories; d; d = d->next)
+    {
+      dir = d->data;
+
+      tmp2 = find_string (dir);
+
+      if (tmp2 == 0 || tmp2 == -1)
+        {
+          tmp2 = tmp;
+          tmp += ALIGN_VALUE (strlen (dir) + 1, 4);
+          /* We're playing a little game with negative
+           * offsets here to handle duplicate strings in
+           * the array, even though that should not
+           * really happen for the directory index.
+           */
+          add_string (dir, -tmp2);
+        }
+      else if (tmp2 < 0)
+        {
+          tmp2 = -tmp2;
+        }
+
+      if (!write_card32 (cache, tmp2))
+       return FALSE;
+    }
+
+  g_assert (offset == ftell (cache));
+  for (d = directories; d; d = d->next)
+    {
+      dir = d->data;
+
+      tmp2 = find_string (dir);
+      g_assert (tmp2 != 0 && tmp2 != -1);
+      if (tmp2 < 0)
+        {
+          tmp2 = -tmp2;
+          g_assert (tmp2 == ftell (cache));
+          add_string (dir, tmp2);
+          if (!write_string (cache, dir))
+           return FALSE;
+        }
+    }
+
+  return TRUE;
+}
+
+static gboolean
+write_file (FILE *cache, GHashTable *files, GList *directories)
+{
+  HashContext context;
+  int new_offset;
+
+  /* Convert the hash table into something looking a bit more
+   * like what we want to write to disk.
+   */
+  context.size = g_spaced_primes_closest (g_hash_table_size (files) / 3);
+  context.nodes = g_new0 (HashNode *, context.size);
+
+  g_hash_table_foreach_remove (files, convert_to_hash, &context);
+
+  /* Now write the file */
+  /* We write 0 as the directory list offset and go
+   * back and change it later */
+  if (!write_header (cache, 0))
+    {
+      g_printerr (_("Failed to write header\n"));
+      return FALSE;
+    }
+
+  if (!write_hash_table (cache, &context, &new_offset))
+    {
+      g_printerr (_("Failed to write hash table\n"));
+      return FALSE;
+    }
+
+  if (!write_dir_index (cache, new_offset, directories))
+    {
+      g_printerr (_("Failed to write folder index\n"));
+      return FALSE;
+    }
+
+  rewind (cache);
+
+  if (!write_header (cache, new_offset))
+    {
+      g_printerr (_("Failed to rewrite header\n"));
+      return FALSE;
+    }
+
+  return TRUE;
+}
+
+static gboolean
+validate_file (const char *file)
+{
+  GMappedFile *map;
+  CacheInfo info;
+
+  map = g_mapped_file_new (file, FALSE, NULL);
+  if (!map)
+    return FALSE;
+
+  info.cache = g_mapped_file_get_contents (map);
+  info.cache_size = g_mapped_file_get_length (map);
+  info.n_directories = 0;
+  info.flags = CHECK_OFFSETS|CHECK_STRINGS|CHECK_PIXBUFS;
+
+  if (!gtk_icon_cache_validate (&info))
+    {
+      g_mapped_file_unref (map);
+      return FALSE;
+    }
+
+  g_mapped_file_unref (map);
+
+  return TRUE;
+}
+
+/**
+ * safe_fclose:
+ * @f: A FILE* stream, must have underlying fd
+ *
+ * Unix defaults for data preservation after system crash
+ * are unspecified, and many systems will eat your data
+ * in this situation unless you explicitly fsync().
+ *
+ * Returns: %TRUE on success, %FALSE on failure, and will set errno()
+ */
+static gboolean
+safe_fclose (FILE *f)
+{
+  int fd = fileno (f);
+  g_assert (fd >= 0);
+  if (fflush (f) == EOF)
+    return FALSE;
+#ifndef G_OS_WIN32
+  if (fsync (fd) < 0)
+    return FALSE;
+#endif
+  if (fclose (f) == EOF)
+    return FALSE;
+  return TRUE;
+}
+
+static void
+build_cache (const char *path)
+{
+  char *cache_path, *tmp_cache_path;
+#ifdef G_OS_WIN32
+  char *bak_cache_path = NULL;
+#endif
+  GHashTable *files;
+  FILE *cache;
+  GStatBuf path_stat, cache_stat;
+  struct utimbuf utime_buf;
+  GList *directories = NULL;
+  int fd;
+  int retry_count = 0;
+#ifndef G_OS_WIN32
+  mode_t mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH;
+#else
+  int mode = _S_IWRITE | _S_IREAD;
+#endif
+#ifndef _O_BINARY
+#define _O_BINARY 0
+#endif
+
+  tmp_cache_path = g_build_filename (path, "."CACHE_NAME, NULL);
+  cache_path = g_build_filename (path, CACHE_NAME, NULL);
+
+opentmp:
+  if ((fd = g_open (tmp_cache_path, O_WRONLY | O_CREAT | O_EXCL | O_TRUNC | _O_BINARY, mode)) == -1)
+    {
+      if (retry_count == 0)
+        {
+          retry_count++;
+          g_remove (tmp_cache_path);
+          goto opentmp;
+        }
+      g_printerr (_("Failed to open file %s : %s\n"), tmp_cache_path, g_strerror (errno));
+      exit (1);
+    }
+
+  cache = fdopen (fd, "wb");
+
+  if (!cache)
+    {
+      g_printerr (_("Failed to write cache file: %s\n"), g_strerror (errno));
+      exit (1);
+    }
+
+  files = g_hash_table_new (g_str_hash, g_str_equal);
+  image_data_hash = g_hash_table_new (g_str_hash, g_str_equal);
+  icon_data_hash = g_hash_table_new (g_str_hash, g_str_equal);
+  string_pool = g_hash_table_new (g_str_hash, g_str_equal);
+
+  directories = scan_directory (path, NULL, files, NULL, 0);
+
+  if (g_hash_table_size (files) == 0)
+    {
+      /* Empty table, just close and remove the file */
+
+      fclose (cache);
+      g_unlink (tmp_cache_path);
+      g_unlink (cache_path);
+      exit (0);
+    }
+
+  /* FIXME: Handle failure */
+  if (!write_file (cache, files, directories))
+    {
+      g_unlink (tmp_cache_path);
+      exit (1);
+    }
+
+  if (!safe_fclose (cache))
+    {
+      g_printerr (_("Failed to write cache file: %s\n"), g_strerror (errno));
+      g_unlink (tmp_cache_path);
+      exit (1);
+    }
+  cache = NULL;
+
+  g_list_free_full (directories, g_free);
+
+  if (!validate_file (tmp_cache_path))
+    {
+      g_printerr (_("The generated cache was invalid.\n"));
+      /*g_unlink (tmp_cache_path);*/
+      exit (1);
+    }
+
+#ifdef G_OS_WIN32
+  if (g_file_test (cache_path, G_FILE_TEST_EXISTS))
+    {
+      bak_cache_path = g_strconcat (cache_path, ".bak", NULL);
+      g_unlink (bak_cache_path);
+      if (g_rename (cache_path, bak_cache_path) == -1)
+       {
+          int errsv = errno;
+
+         g_printerr (_("Could not rename %s to %s: %s, removing %s then.\n"),
+                     cache_path, bak_cache_path,
+                     g_strerror (errsv),
+                     cache_path);
+         g_unlink (cache_path);
+         bak_cache_path = NULL;
+       }
+    }
+#endif
+
+  if (g_rename (tmp_cache_path, cache_path) == -1)
+    {
+      int errsv = errno;
+
+      g_printerr (_("Could not rename %s to %s: %s\n"),
+                 tmp_cache_path, cache_path,
+                 g_strerror (errsv));
+      g_unlink (tmp_cache_path);
+#ifdef G_OS_WIN32
+      if (bak_cache_path != NULL)
+       if (g_rename (bak_cache_path, cache_path) == -1)
+          {
+            errsv = errno;
+
+            g_printerr (_("Could not rename %s back to %s: %s.\n"),
+                        bak_cache_path, cache_path,
+                        g_strerror (errsv));
+          }
+#endif
+      exit (1);
+    }
+#ifdef G_OS_WIN32
+  if (bak_cache_path != NULL)
+    g_unlink (bak_cache_path);
+#endif
+
+  /* Update time */
+  /* FIXME: What do do if an error occurs here? */
+  if (g_stat (path, &path_stat) < 0 ||
+      g_stat (cache_path, &cache_stat))
+    exit (1);
+
+  utime_buf.actime = path_stat.st_atime;
+  utime_buf.modtime = cache_stat.st_mtime;
+#if GLIB_CHECK_VERSION (2, 17, 1)
+  g_utime (path, &utime_buf);
+#else
+  utime (path, &utime_buf);
+#endif
+
+  if (!quiet)
+    g_printerr (_("Cache file created successfully.\n"));
+}
+
+static void
+write_csource (const char *path)
+{
+  char *cache_path;
+  char *data;
+  gsize len;
+  int i;
+
+  cache_path = g_build_filename (path, CACHE_NAME, NULL);
+  if (!g_file_get_contents (cache_path, &data, &len, NULL))
+    exit (1);
+
+  g_printf ("#ifdef __SUNPRO_C\n");
+  g_printf ("#pragma align 4 (%s)\n", var_name);
+  g_printf ("#endif\n");
+
+  g_printf ("#ifdef __GNUC__\n");
+  g_printf ("static const guint8 %s[] __attribute__ ((__aligned__ (4))) = \n", var_name);
+  g_printf ("#else\n");
+  g_printf ("static const guint8 %s[] = \n", var_name);
+  g_printf ("#endif\n");
+
+  g_printf ("{\n");
+  for (i = 0; i < len - 1; i++)
+    {
+      if (i %12 == 0)
+       g_printf ("  ");
+      g_printf ("0x%02x, ", (guint8)data[i]);
+      if (i % 12 == 11)
+        g_printf ("\n");
+    }
+
+  g_printf ("0x%02x\n};\n", (guint8)data[i]);
+}
+
+static GOptionEntry args[] = {
+  { "force", 'f', 0, G_OPTION_ARG_NONE, &force_update, N_("Overwrite an existing cache, even if up to date"), NULL },
+  { "ignore-theme-index", 't', 0, G_OPTION_ARG_NONE, &ignore_theme_index, N_("Don’t check for the existence of index.theme"), NULL },
+  { "index-only", 'i', 0, G_OPTION_ARG_NONE, &index_only, N_("Don’t include image data in the cache"), NULL },
+  { "include-image-data", 0, G_OPTION_FLAG_REVERSE, G_OPTION_ARG_NONE, &index_only, N_("Include image data in the cache"), NULL },
+  { "source", 'c', 0, G_OPTION_ARG_STRING, &var_name, N_("Output a C header file"), "NAME" },
+  { "quiet", 'q', 0, G_OPTION_ARG_NONE, &quiet, N_("Turn off verbose output"), NULL },
+  { "validate", 'v', 0, G_OPTION_ARG_NONE, &validate, N_("Validate existing icon cache"), NULL },
+  { NULL }
+};
+
+static void
+printerr_handler (const char *string)
+{
+  const char *charset;
+
+  fputs (g_get_prgname (), stderr);
+  fputs (": ", stderr);
+  if (g_get_charset (&charset))
+    fputs (string, stderr); /* charset is UTF-8 already */
+  else
+    {
+      char *result;
+
+      result = g_convert_with_fallback (string, -1, charset, "UTF-8", "?", NULL, NULL, NULL);
+
+      if (result)
+        {
+          fputs (result, stderr);
+          g_free (result);
+        }
+
+      fflush (stderr);
+    }
+}
+
+
+int
+main (int argc, char **argv)
+{
+  char *path;
+  GOptionContext *context;
+
+  if (argc < 2)
+    return 0;
+
+  g_set_printerr_handler (printerr_handler);
+
+  setlocale (LC_ALL, "");
+
+#ifdef ENABLE_NLS
+  bindtextdomain (GETTEXT_PACKAGE, GTK_LOCALEDIR);
+#ifdef HAVE_BIND_TEXTDOMAIN_CODESET
+  bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
+#endif
+#endif
+
+  context = g_option_context_new ("ICONPATH");
+  g_option_context_add_main_entries (context, args, GETTEXT_PACKAGE);
+
+  g_option_context_parse (context, &argc, &argv, NULL);
+
+  path = argv[1];
+#ifdef G_OS_WIN32
+  path = g_locale_to_utf8 (path, -1, NULL, NULL, NULL);
+#endif
+
+  if (validate)
+    {
+       char *file = g_build_filename (path, CACHE_NAME, NULL);
+
+       if (!g_file_test (file, G_FILE_TEST_IS_REGULAR))
+         {
+            if (!quiet)
+              g_printerr (_("File not found: %s\n"), file);
+            exit (1);
+         }
+       if (!validate_file (file))
+         {
+           if (!quiet)
+             g_printerr (_("Not a valid icon cache: %s\n"), file);
+           exit (1);
+         }
+       else
+         {
+           exit (0);
+         }
+    }
+
+  if (!ignore_theme_index && !has_theme_index (path))
+    {
+      if (path)
+       {
+         g_printerr (_("No theme index file.\n"));
+       }
+      else
+       {
+         g_printerr (_("No theme index file in “%s”.\n"
+                   "If you really want to create an icon cache here, use --ignore-theme-index.\n"), path);
+       }
+
+      return 1;
+    }
+
+  if (!force_update && is_cache_up_to_date (path))
+    return 0;
+
+  replace_backslashes_with_slashes (path);
+  build_cache (path);
+
+  if (strcmp (var_name, "-") != 0)
+    write_csource (path);
+
+  return 0;
+}